js_stack 1.10.0 → 1.11.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -0
  3. data/README.md +6 -6
  4. data/js_stack.gemspec +0 -1
  5. data/lib/js_stack/version.rb +1 -1
  6. data/lib/js_stack.rb +0 -1
  7. data/vendor/assets/javascripts/js_stack/base/backbone/1.2.3.js +1894 -0
  8. data/vendor/assets/javascripts/js_stack/base/backbone.js +1 -1
  9. data/vendor/assets/javascripts/js_stack/base/marionette/{2.4.1.js → 2.4.3.js} +265 -138
  10. data/vendor/assets/javascripts/js_stack/base/marionette.js +1 -1
  11. data/vendor/assets/javascripts/js_stack/plugins/backbone/stickit/{0.9.0.js → 0.9.2.js} +1 -1
  12. data/vendor/assets/javascripts/js_stack/plugins/backbone.stickit.js +1 -1
  13. data/vendor/assets/javascripts/js_stack/plugins/moment/2.10.6.js +3195 -0
  14. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/af.js +72 -0
  15. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/ar-ma.js +58 -0
  16. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/ar-sa.js +102 -0
  17. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/ar-tn.js +56 -0
  18. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/ar.js +135 -0
  19. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/az.js +103 -0
  20. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/be.js +146 -0
  21. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/bg.js +89 -0
  22. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/bn.js +112 -0
  23. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/bo.js +109 -0
  24. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/br.js +106 -0
  25. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/bs.js +140 -0
  26. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/ca.js +78 -0
  27. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/cs.js +156 -0
  28. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/cv.js +62 -0
  29. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/cy.js +78 -0
  30. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/da.js +59 -0
  31. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/de-at.js +75 -0
  32. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/de.js +74 -0
  33. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/el.js +93 -0
  34. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/en-au.js +65 -0
  35. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/en-ca.js +62 -0
  36. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/en-gb.js +66 -0
  37. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/eo.js +72 -0
  38. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/es.js +78 -0
  39. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/et.js +79 -0
  40. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/eu.js +63 -0
  41. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/fa.js +104 -0
  42. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/fi.js +106 -0
  43. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/fo.js +59 -0
  44. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/fr-ca.js +57 -0
  45. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/fr.js +61 -0
  46. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/fy.js +70 -0
  47. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/gl.js +74 -0
  48. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/he.js +81 -0
  49. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/hi.js +122 -0
  50. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/hr.js +139 -0
  51. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/hu.js +108 -0
  52. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/hy-am.js +110 -0
  53. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/id.js +82 -0
  54. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/is.js +126 -0
  55. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/it.js +69 -0
  56. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/ja.js +64 -0
  57. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/jv.js +82 -0
  58. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/ka.js +102 -0
  59. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/km.js +57 -0
  60. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/ko.js +67 -0
  61. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/lb.js +133 -0
  62. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/lt.js +124 -0
  63. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/lv.js +95 -0
  64. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/me.js +108 -0
  65. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/mk.js +89 -0
  66. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/ml.js +70 -0
  67. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/mr.js +120 -0
  68. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/ms-my.js +81 -0
  69. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/ms.js +81 -0
  70. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/my.js +92 -0
  71. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/nb.js +60 -0
  72. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/ne.js +122 -0
  73. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/nl.js +70 -0
  74. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/nn.js +59 -0
  75. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/pl.js +104 -0
  76. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/pt-br.js +59 -0
  77. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/pt.js +63 -0
  78. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/ro.js +73 -0
  79. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/ru.js +163 -0
  80. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/si.js +64 -0
  81. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/sk.js +157 -0
  82. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/sl.js +159 -0
  83. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/sq.js +68 -0
  84. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/sr-cyrl.js +107 -0
  85. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/sr.js +107 -0
  86. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/sv.js +66 -0
  87. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/ta.js +94 -0
  88. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/th.js +64 -0
  89. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/tl-ph.js +61 -0
  90. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/tr.js +89 -0
  91. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/tzl.js +84 -0
  92. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/tzm-latn.js +57 -0
  93. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/tzm.js +57 -0
  94. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/uk.js +152 -0
  95. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/uz.js +57 -0
  96. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/vi.js +65 -0
  97. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/zh-cn.js +126 -0
  98. data/vendor/assets/javascripts/js_stack/plugins/moment/locale/zh-tw.js +100 -0
  99. data/vendor/assets/javascripts/js_stack/plugins/moment.js +1 -1
  100. data/vendor/assets/javascripts/js_stack/plugins/underscore/string/3.2.2.js +1186 -0
  101. data/vendor/assets/javascripts/js_stack/plugins/underscore.string.js +1 -1
  102. metadata +93 -20
@@ -1,6 +1,6 @@
1
1
  // MarionetteJS (Backbone.Marionette)
2
2
  // ----------------------------------
3
- // v2.4.1
3
+ // v2.4.3
4
4
  //
5
5
  // Copyright (c)2015 Derick Bailey, Muted Solutions, LLC.
6
6
  // Distributed under MIT license
@@ -38,7 +38,7 @@
38
38
  /* istanbul ignore next */
39
39
  // Backbone.BabySitter
40
40
  // -------------------
41
- // v0.1.6
41
+ // v0.1.10
42
42
  //
43
43
  // Copyright (c)2015 Derick Bailey, Muted Solutions, LLC.
44
44
  // Distributed under MIT license
@@ -167,7 +167,7 @@
167
167
  // return the public API
168
168
  return Container;
169
169
  }(Backbone, _);
170
- Backbone.ChildViewContainer.VERSION = "0.1.6";
170
+ Backbone.ChildViewContainer.VERSION = "0.1.10";
171
171
  Backbone.ChildViewContainer.noConflict = function() {
172
172
  Backbone.ChildViewContainer = previousChildViewContainer;
173
173
  return this;
@@ -178,9 +178,9 @@
178
178
  /* istanbul ignore next */
179
179
  // Backbone.Wreqr (Backbone.Marionette)
180
180
  // ----------------------------------
181
- // v1.3.1
181
+ // v1.3.5
182
182
  //
183
- // Copyright (c)2014 Derick Bailey, Muted Solutions, LLC.
183
+ // Copyright (c)2015 Derick Bailey, Muted Solutions, LLC.
184
184
  // Distributed under MIT license
185
185
  //
186
186
  // http://github.com/marionettejs/backbone.wreqr
@@ -188,7 +188,7 @@
188
188
  "use strict";
189
189
  var previousWreqr = Backbone.Wreqr;
190
190
  var Wreqr = Backbone.Wreqr = {};
191
- Backbone.Wreqr.VERSION = "1.3.1";
191
+ Backbone.Wreqr.VERSION = "1.3.5";
192
192
  Backbone.Wreqr.noConflict = function() {
193
193
  Backbone.Wreqr = previousWreqr;
194
194
  return this;
@@ -245,8 +245,7 @@
245
245
  return;
246
246
  }
247
247
  return function() {
248
- var args = Array.prototype.slice.apply(arguments);
249
- return config.callback.apply(config.context, args);
248
+ return config.callback.apply(config.context, arguments);
250
249
  };
251
250
  },
252
251
  // Remove a handler for the specified name
@@ -312,7 +311,7 @@
312
311
  //
313
312
  // A simple command pattern implementation. Register a command
314
313
  // handler and execute it.
315
- Wreqr.Commands = function(Wreqr) {
314
+ Wreqr.Commands = function(Wreqr, _) {
316
315
  "use strict";
317
316
  return Wreqr.Handlers.extend({
318
317
  // default storage type
@@ -321,13 +320,12 @@
321
320
  this.options = options || {};
322
321
  this._initializeStorage(this.options);
323
322
  this.on("handler:add", this._executeCommands, this);
324
- var args = Array.prototype.slice.call(arguments);
325
- Wreqr.Handlers.prototype.constructor.apply(this, args);
323
+ Wreqr.Handlers.prototype.constructor.apply(this, arguments);
326
324
  },
327
325
  // Execute a named command with the supplied args
328
- execute: function(name, args) {
326
+ execute: function(name) {
329
327
  name = arguments[0];
330
- args = Array.prototype.slice.call(arguments, 1);
328
+ var args = _.rest(arguments);
331
329
  if (this.hasHandler(name)) {
332
330
  this.getHandler(name).apply(this, args);
333
331
  } else {
@@ -356,24 +354,22 @@
356
354
  this.storage = storage;
357
355
  }
358
356
  });
359
- }(Wreqr);
357
+ }(Wreqr, _);
360
358
  // Wreqr.RequestResponse
361
359
  // ---------------------
362
360
  //
363
361
  // A simple request/response implementation. Register a
364
362
  // request handler, and return a response from it
365
- Wreqr.RequestResponse = function(Wreqr) {
363
+ Wreqr.RequestResponse = function(Wreqr, _) {
366
364
  "use strict";
367
365
  return Wreqr.Handlers.extend({
368
- request: function() {
369
- var name = arguments[0];
370
- var args = Array.prototype.slice.call(arguments, 1);
366
+ request: function(name) {
371
367
  if (this.hasHandler(name)) {
372
- return this.getHandler(name).apply(this, args);
368
+ return this.getHandler(name).apply(this, _.rest(arguments));
373
369
  }
374
370
  }
375
371
  });
376
- }(Wreqr);
372
+ }(Wreqr, _);
377
373
  // Event Aggregator
378
374
  // ----------------
379
375
  // A pub-sub object that can be used to decouple various parts
@@ -440,7 +436,7 @@
440
436
  // --------------
441
437
  //
442
438
  // An object that lets you communicate with many channels.
443
- Wreqr.radio = function(Wreqr) {
439
+ Wreqr.radio = function(Wreqr, _) {
444
440
  "use strict";
445
441
  var Radio = function() {
446
442
  this._channels = {};
@@ -480,12 +476,11 @@
480
476
  var proxyMethod = function(radio, system, method) {
481
477
  return function(channelName) {
482
478
  var messageSystem = radio._getChannel(channelName)[system];
483
- var args = Array.prototype.slice.call(arguments, 1);
484
- return messageSystem[method].apply(messageSystem, args);
479
+ return messageSystem[method].apply(messageSystem, _.rest(arguments));
485
480
  };
486
481
  };
487
482
  return new Radio();
488
- }(Wreqr);
483
+ }(Wreqr, _);
489
484
  return Backbone.Wreqr;
490
485
  })(Backbone, _);
491
486
 
@@ -494,7 +489,7 @@
494
489
 
495
490
  var Marionette = Backbone.Marionette = {};
496
491
 
497
- Marionette.VERSION = '2.4.1';
492
+ Marionette.VERSION = '2.4.3';
498
493
 
499
494
  Marionette.noConflict = function() {
500
495
  root.Marionette = previousMarionette;
@@ -734,6 +729,8 @@
734
729
  // re-rendered.
735
730
 
736
731
  Marionette.MonitorDOMRefresh = function(view) {
732
+ if (view._isDomRefreshMonitored) { return; }
733
+ view._isDomRefreshMonitored = true;
737
734
 
738
735
  // track when the view has been shown in the DOM,
739
736
  // using a Marionette.Region (or by other means of triggering "show")
@@ -751,9 +748,7 @@
751
748
  // Trigger the "dom:refresh" event and corresponding "onDomRefresh" method
752
749
  function triggerDOMRefresh() {
753
750
  if (view._isShown && view._isRendered && Marionette.isNodeAttached(view.el)) {
754
- if (_.isFunction(view.triggerMethod)) {
755
- view.triggerMethod('dom:refresh');
756
- }
751
+ Marionette.triggerMethodOn(view, 'dom:refresh', view);
757
752
  }
758
753
  }
759
754
 
@@ -1094,6 +1089,7 @@
1094
1089
  }
1095
1090
 
1096
1091
  this._ensureViewIsIntact(view);
1092
+ Marionette.MonitorDOMRefresh(view);
1097
1093
 
1098
1094
  var showOptions = options || {};
1099
1095
  var isDifferentView = view !== this.currentView;
@@ -1138,7 +1134,8 @@
1138
1134
  // to the currentView since once a view has been destroyed
1139
1135
  // we can not reuse it.
1140
1136
  view.once('destroy', this.empty, this);
1141
- view.render();
1137
+
1138
+ this._renderView(view);
1142
1139
 
1143
1140
  view._parent = this;
1144
1141
 
@@ -1161,10 +1158,12 @@
1161
1158
  // as it's a potentially-slow method
1162
1159
  var displayedViews = [];
1163
1160
 
1164
- var triggerBeforeAttach = showOptions.triggerBeforeAttach || this.triggerBeforeAttach;
1165
- var triggerAttach = showOptions.triggerAttach || this.triggerAttach;
1161
+ var attachOptions = _.extend({
1162
+ triggerBeforeAttach: this.triggerBeforeAttach,
1163
+ triggerAttach: this.triggerAttach
1164
+ }, showOptions);
1166
1165
 
1167
- if (attachedRegion && triggerBeforeAttach) {
1166
+ if (attachedRegion && attachOptions.triggerBeforeAttach) {
1168
1167
  displayedViews = this._displayedViews(view);
1169
1168
  this._triggerAttach(displayedViews, 'before:');
1170
1169
  }
@@ -1172,7 +1171,7 @@
1172
1171
  this.attachHtml(view);
1173
1172
  this.currentView = view;
1174
1173
 
1175
- if (attachedRegion && triggerAttach) {
1174
+ if (attachedRegion && attachOptions.triggerAttach) {
1176
1175
  displayedViews = this._displayedViews(view);
1177
1176
  this._triggerAttach(displayedViews);
1178
1177
  }
@@ -1204,6 +1203,16 @@
1204
1203
  return _.union([view], _.result(view, '_getNestedViews') || []);
1205
1204
  },
1206
1205
 
1206
+ _renderView: function(view) {
1207
+ if (!view.supportsRenderLifecycle) {
1208
+ Marionette.triggerMethodOn(view, 'before:render', view);
1209
+ }
1210
+ view.render();
1211
+ if (!view.supportsRenderLifecycle) {
1212
+ Marionette.triggerMethodOn(view, 'render', view);
1213
+ }
1214
+ },
1215
+
1207
1216
  _ensureElement: function() {
1208
1217
  if (!_.isObject(this.el)) {
1209
1218
  this.$el = this.getEl(this.el);
@@ -1256,7 +1265,8 @@
1256
1265
  empty: function(options) {
1257
1266
  var view = this.currentView;
1258
1267
 
1259
- var preventDestroy = Marionette._getValue(options, 'preventDestroy', this);
1268
+ var emptyOptions = options || {};
1269
+ var preventDestroy = !!emptyOptions.preventDestroy;
1260
1270
  // If there is no view in the region
1261
1271
  // we should not remove anything
1262
1272
  if (!view) { return; }
@@ -1282,16 +1292,23 @@
1282
1292
  // on the view (if showing a raw Backbone view or a Marionette View)
1283
1293
  _destroyView: function() {
1284
1294
  var view = this.currentView;
1295
+ if (view.isDestroyed) { return; }
1285
1296
 
1286
- if (view.destroy && !view.isDestroyed) {
1297
+ if (!view.supportsDestroyLifecycle) {
1298
+ Marionette.triggerMethodOn(view, 'before:destroy', view);
1299
+ }
1300
+ if (view.destroy) {
1287
1301
  view.destroy();
1288
- } else if (view.remove) {
1302
+ } else {
1289
1303
  view.remove();
1290
1304
 
1291
1305
  // appending isDestroyed to raw Backbone View allows regions
1292
1306
  // to throw a ViewDestroyedError for this view
1293
1307
  view.isDestroyed = true;
1294
1308
  }
1309
+ if (!view.supportsDestroyLifecycle) {
1310
+ Marionette.triggerMethodOn(view, 'destroy', view);
1311
+ }
1295
1312
  },
1296
1313
 
1297
1314
  // Attach an existing view to the region. This
@@ -1299,6 +1316,10 @@
1299
1316
  // and will not replace the current HTML for the `el`
1300
1317
  // of the region.
1301
1318
  attachView: function(view) {
1319
+ if (this.currentView) {
1320
+ delete this.currentView._parent;
1321
+ }
1322
+ view._parent = this;
1302
1323
  this.currentView = view;
1303
1324
  return this;
1304
1325
  },
@@ -1590,16 +1611,15 @@
1590
1611
  // using a template-loader plugin as described here:
1591
1612
  // https://github.com/marionettejs/backbone.marionette/wiki/Using-marionette-with-requirejs
1592
1613
  loadTemplate: function(templateId, options) {
1593
- var template = Backbone.$(templateId).html();
1614
+ var $template = Backbone.$(templateId);
1594
1615
 
1595
- if (!template || template.length === 0) {
1616
+ if (!$template.length) {
1596
1617
  throw new Marionette.Error({
1597
1618
  name: 'NoTemplateError',
1598
1619
  message: 'Could not find template: "' + templateId + '"'
1599
1620
  });
1600
1621
  }
1601
-
1602
- return template;
1622
+ return $template.html();
1603
1623
  },
1604
1624
 
1605
1625
  // Pre-compile the template before caching it. Override
@@ -1644,9 +1664,11 @@
1644
1664
  // The core view class that other Marionette views extend from.
1645
1665
  Marionette.View = Backbone.View.extend({
1646
1666
  isDestroyed: false,
1667
+ supportsRenderLifecycle: true,
1668
+ supportsDestroyLifecycle: true,
1647
1669
 
1648
1670
  constructor: function(options) {
1649
- _.bindAll(this, 'render');
1671
+ this.render = _.bind(this.render, this);
1650
1672
 
1651
1673
  options = Marionette._getValue(options, this);
1652
1674
 
@@ -1866,14 +1888,13 @@
1866
1888
  // Internal method to create an event handler for a given `triggerDef` like
1867
1889
  // 'click:foo'
1868
1890
  _buildViewTrigger: function(triggerDef) {
1869
- var hasOptions = _.isObject(triggerDef);
1870
1891
 
1871
- var options = _.defaults({}, (hasOptions ? triggerDef : {}), {
1892
+ var options = _.defaults({}, triggerDef, {
1872
1893
  preventDefault: true,
1873
1894
  stopPropagation: true
1874
1895
  });
1875
1896
 
1876
- var eventName = hasOptions ? options.event : triggerDef;
1897
+ var eventName = _.isObject(triggerDef) ? options.event : triggerDef;
1877
1898
 
1878
1899
  return function(e) {
1879
1900
  if (e) {
@@ -1936,15 +1957,16 @@
1936
1957
  // invoke triggerMethod on parent view
1937
1958
  var eventPrefix = Marionette.getOption(layoutView, 'childViewEventPrefix');
1938
1959
  var prefixedEventName = eventPrefix + ':' + eventName;
1960
+ var callArgs = [this].concat(args);
1939
1961
 
1940
- Marionette._triggerMethod(layoutView, [prefixedEventName, this].concat(args));
1962
+ Marionette._triggerMethod(layoutView, prefixedEventName, callArgs);
1941
1963
 
1942
1964
  // call the parent view's childEvents handler
1943
1965
  var childEvents = Marionette.getOption(layoutView, 'childEvents');
1944
1966
  var normalizedChildEvents = layoutView.normalizeMethods(childEvents);
1945
1967
 
1946
- if (!!normalizedChildEvents && _.isFunction(normalizedChildEvents[eventName])) {
1947
- normalizedChildEvents[eventName].apply(layoutView, [this].concat(args));
1968
+ if (normalizedChildEvents && _.isFunction(normalizedChildEvents[eventName])) {
1969
+ normalizedChildEvents[eventName].apply(layoutView, callArgs);
1948
1970
  }
1949
1971
  },
1950
1972
 
@@ -2117,7 +2139,7 @@
2117
2139
  }
2118
2140
  });
2119
2141
 
2120
- /* jshint maxstatements: 14 */
2142
+ /* jshint maxstatements: 20, maxcomplexity: 7 */
2121
2143
 
2122
2144
  // Collection View
2123
2145
  // ---------------
@@ -2141,14 +2163,17 @@
2141
2163
  // option to pass `{comparator: compFunction()}` to allow the `CollectionView`
2142
2164
  // to use a custom sort order for the collection.
2143
2165
  constructor: function(options) {
2144
-
2145
2166
  this.once('render', this._initialEvents);
2146
2167
  this._initChildViewStorage();
2147
2168
 
2148
2169
  Marionette.View.apply(this, arguments);
2149
2170
 
2150
- this.on('show', this._onShowCalled);
2151
-
2171
+ this.on({
2172
+ 'before:show': this._onBeforeShowCalled,
2173
+ 'show': this._onShowCalled,
2174
+ 'before:attach': this._onBeforeAttachCalled,
2175
+ 'attach': this._onAttachCalled
2176
+ });
2152
2177
  this.initRenderBuffer();
2153
2178
  },
2154
2179
 
@@ -2165,33 +2190,38 @@
2165
2190
  },
2166
2191
 
2167
2192
  endBuffering: function() {
2168
- this.isBuffering = false;
2169
- this._triggerBeforeShowBufferedChildren();
2170
-
2171
- this.attachBuffer(this);
2193
+ // Only trigger attach if already shown and attached, otherwise Region#show() handles this.
2194
+ var canTriggerAttach = this._isShown && Marionette.isNodeAttached(this.el);
2195
+ var nestedViews;
2172
2196
 
2173
- this._triggerShowBufferedChildren();
2174
- this.initRenderBuffer();
2175
- },
2197
+ this.isBuffering = false;
2176
2198
 
2177
- _triggerBeforeShowBufferedChildren: function() {
2178
2199
  if (this._isShown) {
2179
- _.each(this._bufferedChildren, _.partial(this._triggerMethodOnChild, 'before:show'));
2200
+ this._triggerMethodMany(this._bufferedChildren, this, 'before:show');
2201
+ }
2202
+ if (canTriggerAttach && this._triggerBeforeAttach) {
2203
+ nestedViews = this._getNestedViews();
2204
+ this._triggerMethodMany(nestedViews, this, 'before:attach');
2180
2205
  }
2181
- },
2182
2206
 
2183
- _triggerShowBufferedChildren: function() {
2184
- if (this._isShown) {
2185
- _.each(this._bufferedChildren, _.partial(this._triggerMethodOnChild, 'show'));
2207
+ this.attachBuffer(this, this._createBuffer());
2186
2208
 
2187
- this._bufferedChildren = [];
2209
+ if (canTriggerAttach && this._triggerAttach) {
2210
+ nestedViews = this._getNestedViews();
2211
+ this._triggerMethodMany(nestedViews, this, 'attach');
2188
2212
  }
2213
+ if (this._isShown) {
2214
+ this._triggerMethodMany(this._bufferedChildren, this, 'show');
2215
+ }
2216
+ this.initRenderBuffer();
2189
2217
  },
2190
2218
 
2191
- // Internal method for _.each loops to call `Marionette.triggerMethodOn` on
2192
- // a child view
2193
- _triggerMethodOnChild: function(event, childView) {
2194
- Marionette.triggerMethodOn(childView, event);
2219
+ _triggerMethodMany: function(targets, source, eventName) {
2220
+ var args = _.drop(arguments, 3);
2221
+
2222
+ _.each(targets, function(target) {
2223
+ Marionette.triggerMethodOn.apply(target, [target, eventName, target, source].concat(args));
2224
+ });
2195
2225
  },
2196
2226
 
2197
2227
  // Configured the initial events that the collection view
@@ -2210,11 +2240,12 @@
2210
2240
 
2211
2241
  // Handle a child added to the collection
2212
2242
  _onCollectionAdd: function(child, collection, opts) {
2213
- var index;
2214
- if (opts.at !== undefined) {
2215
- index = opts.at;
2216
- } else {
2217
- index = _.indexOf(this._filteredSortedModels(), child);
2243
+ // `index` is present when adding with `at` since BB 1.2; indexOf fallback for < 1.2
2244
+ var index = opts.at !== undefined && (opts.index || collection.indexOf(child));
2245
+
2246
+ // When filtered or when there is no initial index, calculate index.
2247
+ if (this.getOption('filter') || index === false) {
2248
+ index = _.indexOf(this._filteredSortedModels(index), child);
2218
2249
  }
2219
2250
 
2220
2251
  if (this._shouldAddChild(child, index)) {
@@ -2231,8 +2262,29 @@
2231
2262
  this.checkEmpty();
2232
2263
  },
2233
2264
 
2265
+ _onBeforeShowCalled: function() {
2266
+ // Reset attach event flags at the top of the Region#show() event lifecycle; if the Region's
2267
+ // show() options permit onBeforeAttach/onAttach events, these flags will be set true again.
2268
+ this._triggerBeforeAttach = this._triggerAttach = false;
2269
+ this.children.each(function(childView) {
2270
+ Marionette.triggerMethodOn(childView, 'before:show', childView);
2271
+ });
2272
+ },
2273
+
2234
2274
  _onShowCalled: function() {
2235
- this.children.each(_.partial(this._triggerMethodOnChild, 'show'));
2275
+ this.children.each(function(childView) {
2276
+ Marionette.triggerMethodOn(childView, 'show', childView);
2277
+ });
2278
+ },
2279
+
2280
+ // If during Region#show() onBeforeAttach was fired, continue firing it for child views
2281
+ _onBeforeAttachCalled: function() {
2282
+ this._triggerBeforeAttach = true;
2283
+ },
2284
+
2285
+ // If during Region#show() onAttach was fired, continue firing it for child views
2286
+ _onAttachCalled: function() {
2287
+ this._triggerAttach = true;
2236
2288
  },
2237
2289
 
2238
2290
  // Render children views. Override this method to
@@ -2265,8 +2317,10 @@
2265
2317
  this.render();
2266
2318
  } else {
2267
2319
  // get the DOM nodes in the same order as the models
2268
- var els = _.map(models, function(model) {
2269
- return children.findByModel(model).el;
2320
+ var els = _.map(models, function(model, index) {
2321
+ var view = children.findByModel(model);
2322
+ view._index = index;
2323
+ return view.el;
2270
2324
  });
2271
2325
 
2272
2326
  // since append moves elements that are already in the DOM,
@@ -2319,7 +2373,7 @@
2319
2373
  // process
2320
2374
  _renderChildren: function() {
2321
2375
  this.destroyEmptyView();
2322
- this.destroyChildren();
2376
+ this.destroyChildren({checkEmpty: false});
2323
2377
 
2324
2378
  if (this.isEmpty(this.collection)) {
2325
2379
  this.showEmptyView();
@@ -2331,7 +2385,7 @@
2331
2385
  this.triggerMethod('render:collection', this);
2332
2386
 
2333
2387
  // If we have shown children and none have passed the filter, show the empty view
2334
- if (this.children.isEmpty()) {
2388
+ if (this.children.isEmpty() && this.getOption('filter')) {
2335
2389
  this.showEmptyView();
2336
2390
  }
2337
2391
  }
@@ -2350,18 +2404,22 @@
2350
2404
  },
2351
2405
 
2352
2406
  // Allow the collection to be sorted by a custom view comparator
2353
- _filteredSortedModels: function() {
2354
- var models;
2407
+ _filteredSortedModels: function(addedAt) {
2355
2408
  var viewComparator = this.getViewComparator();
2409
+ var models = this.collection.models;
2410
+ addedAt = Math.min(Math.max(addedAt, 0), models.length - 1);
2356
2411
 
2357
2412
  if (viewComparator) {
2358
- if (_.isString(viewComparator) || viewComparator.length === 1) {
2359
- models = this.collection.sortBy(viewComparator, this);
2360
- } else {
2361
- models = _.clone(this.collection.models).sort(_.bind(viewComparator, this));
2413
+ var addedModel;
2414
+ // Preserve `at` location, even for a sorted view
2415
+ if (addedAt) {
2416
+ addedModel = models[addedAt];
2417
+ models = models.slice(0, addedAt).concat(models.slice(addedAt + 1));
2418
+ }
2419
+ models = this._sortModelsBy(models, viewComparator);
2420
+ if (addedModel) {
2421
+ models.splice(addedAt, 0, addedModel);
2362
2422
  }
2363
- } else {
2364
- models = this.collection.models;
2365
2423
  }
2366
2424
 
2367
2425
  // Filter after sorting in case the filter uses the index
@@ -2374,6 +2432,18 @@
2374
2432
  return models;
2375
2433
  },
2376
2434
 
2435
+ _sortModelsBy: function(models, comparator) {
2436
+ if (typeof comparator === 'string') {
2437
+ return _.sortBy(models, function(model) {
2438
+ return model.get(comparator);
2439
+ }, this);
2440
+ } else if (comparator.length === 1) {
2441
+ return _.sortBy(models, comparator, this);
2442
+ } else {
2443
+ return models.sort(_.bind(comparator, this));
2444
+ }
2445
+ },
2446
+
2377
2447
  // Internal method to show an empty view in place of
2378
2448
  // a collection of child views, when the collection is empty
2379
2449
  showEmptyView: function() {
@@ -2413,6 +2483,10 @@
2413
2483
  // but "add:child" events are not fired, and the event from
2414
2484
  // emptyView are not forwarded
2415
2485
  addEmptyView: function(child, EmptyView) {
2486
+ // Only trigger attach if already shown, attached, and not buffering, otherwise endBuffer() or
2487
+ // Region#show() handles this.
2488
+ var canTriggerAttach = this._isShown && !this.isBuffering && Marionette.isNodeAttached(this.el);
2489
+ var nestedViews;
2416
2490
 
2417
2491
  // get the emptyViewOptions, falling back to childViewOptions
2418
2492
  var emptyViewOptions = this.getOption('emptyViewOptions') ||
@@ -2430,23 +2504,32 @@
2430
2504
  // Proxy emptyView events
2431
2505
  this.proxyChildEvents(view);
2432
2506
 
2433
- // trigger the 'before:show' event on `view` if the collection view
2434
- // has already been shown
2435
- if (this._isShown) {
2436
- Marionette.triggerMethodOn(view, 'before:show');
2437
- }
2507
+ view.once('render', function() {
2508
+ // trigger the 'before:show' event on `view` if the collection view has already been shown
2509
+ if (this._isShown) {
2510
+ Marionette.triggerMethodOn(view, 'before:show', view);
2511
+ }
2438
2512
 
2439
- // Store the `emptyView` like a `childView` so we can properly
2440
- // remove and/or close it later
2441
- this.children.add(view);
2513
+ // Trigger `before:attach` following `render` to avoid adding logic and event triggers
2514
+ // to public method `renderChildView()`.
2515
+ if (canTriggerAttach && this._triggerBeforeAttach) {
2516
+ nestedViews = this._getViewAndNested(view);
2517
+ this._triggerMethodMany(nestedViews, this, 'before:attach');
2518
+ }
2519
+ }, this);
2442
2520
 
2443
- // Render it and show it
2521
+ // Store the `emptyView` like a `childView` so we can properly remove and/or close it later
2522
+ this.children.add(view);
2444
2523
  this.renderChildView(view, this._emptyViewIndex);
2445
2524
 
2446
- // call the 'show' method if the collection view
2447
- // has already been shown
2525
+ // Trigger `attach`
2526
+ if (canTriggerAttach && this._triggerAttach) {
2527
+ nestedViews = this._getViewAndNested(view);
2528
+ this._triggerMethodMany(nestedViews, this, 'attach');
2529
+ }
2530
+ // call the 'show' method if the collection view has already been shown
2448
2531
  if (this._isShown) {
2449
- Marionette.triggerMethodOn(view, 'show');
2532
+ Marionette.triggerMethodOn(view, 'show', view);
2450
2533
  }
2451
2534
  },
2452
2535
 
@@ -2482,7 +2565,9 @@
2482
2565
  // increment indices of views after this one
2483
2566
  this._updateIndices(view, true, index);
2484
2567
 
2568
+ this.triggerMethod('before:add:child', view);
2485
2569
  this._addChildView(view, index);
2570
+ this.triggerMethod('add:child', view);
2486
2571
 
2487
2572
  view._parent = this;
2488
2573
 
@@ -2512,32 +2597,52 @@
2512
2597
  // Internal Method. Add the view to children and render it at
2513
2598
  // the given index.
2514
2599
  _addChildView: function(view, index) {
2600
+ // Only trigger attach if already shown, attached, and not buffering, otherwise endBuffer() or
2601
+ // Region#show() handles this.
2602
+ var canTriggerAttach = this._isShown && !this.isBuffering && Marionette.isNodeAttached(this.el);
2603
+ var nestedViews;
2604
+
2515
2605
  // set up the child view event forwarding
2516
2606
  this.proxyChildEvents(view);
2517
2607
 
2518
- this.triggerMethod('before:add:child', view);
2608
+ view.once('render', function() {
2609
+ // trigger the 'before:show' event on `view` if the collection view has already been shown
2610
+ if (this._isShown && !this.isBuffering) {
2611
+ Marionette.triggerMethodOn(view, 'before:show', view);
2612
+ }
2519
2613
 
2520
- // trigger the 'before:show' event on `view` if the collection view
2521
- // has already been shown
2522
- if (this._isShown && !this.isBuffering) {
2523
- Marionette.triggerMethodOn(view, 'before:show');
2524
- }
2614
+ // Trigger `before:attach` following `render` to avoid adding logic and event triggers
2615
+ // to public method `renderChildView()`.
2616
+ if (canTriggerAttach && this._triggerBeforeAttach) {
2617
+ nestedViews = this._getViewAndNested(view);
2618
+ this._triggerMethodMany(nestedViews, this, 'before:attach');
2619
+ }
2620
+ }, this);
2525
2621
 
2526
- // Store the child view itself so we can properly
2527
- // remove and/or destroy it later
2622
+ // Store the child view itself so we can properly remove and/or destroy it later
2528
2623
  this.children.add(view);
2529
2624
  this.renderChildView(view, index);
2530
2625
 
2626
+ // Trigger `attach`
2627
+ if (canTriggerAttach && this._triggerAttach) {
2628
+ nestedViews = this._getViewAndNested(view);
2629
+ this._triggerMethodMany(nestedViews, this, 'attach');
2630
+ }
2631
+ // Trigger `show`
2531
2632
  if (this._isShown && !this.isBuffering) {
2532
- Marionette.triggerMethodOn(view, 'show');
2633
+ Marionette.triggerMethodOn(view, 'show', view);
2533
2634
  }
2534
-
2535
- this.triggerMethod('add:child', view);
2536
2635
  },
2537
2636
 
2538
2637
  // render the child view
2539
2638
  renderChildView: function(view, index) {
2639
+ if (!view.supportsRenderLifecycle) {
2640
+ Marionette.triggerMethodOn(view, 'before:render', view);
2641
+ }
2540
2642
  view.render();
2643
+ if (!view.supportsRenderLifecycle) {
2644
+ Marionette.triggerMethodOn(view, 'render', view);
2645
+ }
2541
2646
  this.attachHtml(this, view, index);
2542
2647
  return view;
2543
2648
  },
@@ -2545,7 +2650,9 @@
2545
2650
  // Build a `childView` for a model in the collection.
2546
2651
  buildChildView: function(child, ChildViewClass, childViewOptions) {
2547
2652
  var options = _.extend({model: child}, childViewOptions);
2548
- return new ChildViewClass(options);
2653
+ var childView = new ChildViewClass(options);
2654
+ Marionette.MonitorDOMRefresh(childView);
2655
+ return childView;
2549
2656
  },
2550
2657
 
2551
2658
  // Remove the child view and destroy it.
@@ -2553,25 +2660,30 @@
2553
2660
  // later views in the collection in order to keep
2554
2661
  // the children in sync with the collection.
2555
2662
  removeChildView: function(view) {
2663
+ if (!view) { return view; }
2556
2664
 
2557
- if (view) {
2558
- this.triggerMethod('before:remove:child', view);
2665
+ this.triggerMethod('before:remove:child', view);
2559
2666
 
2560
- // call 'destroy' or 'remove', depending on which is found
2561
- if (view.destroy) {
2562
- view.destroy();
2563
- } else if (view.remove) {
2564
- view.remove();
2565
- }
2667
+ if (!view.supportsDestroyLifecycle) {
2668
+ Marionette.triggerMethodOn(view, 'before:destroy', view);
2669
+ }
2670
+ // call 'destroy' or 'remove', depending on which is found
2671
+ if (view.destroy) {
2672
+ view.destroy();
2673
+ } else {
2674
+ view.remove();
2675
+ }
2676
+ if (!view.supportsDestroyLifecycle) {
2677
+ Marionette.triggerMethodOn(view, 'destroy', view);
2678
+ }
2566
2679
 
2567
- delete view._parent;
2568
- this.stopListening(view);
2569
- this.children.remove(view);
2570
- this.triggerMethod('remove:child', view);
2680
+ delete view._parent;
2681
+ this.stopListening(view);
2682
+ this.children.remove(view);
2683
+ this.triggerMethod('remove:child', view);
2571
2684
 
2572
- // decrement the index of views after this one
2573
- this._updateIndices(view, false);
2574
- }
2685
+ // decrement the index of views after this one
2686
+ this._updateIndices(view, false);
2575
2687
 
2576
2688
  return view;
2577
2689
  },
@@ -2589,14 +2701,14 @@
2589
2701
  },
2590
2702
 
2591
2703
  // You might need to override this if you've overridden attachHtml
2592
- attachBuffer: function(collectionView) {
2593
- collectionView.$el.append(this._createBuffer(collectionView));
2704
+ attachBuffer: function(collectionView, buffer) {
2705
+ collectionView.$el.append(buffer);
2594
2706
  },
2595
2707
 
2596
2708
  // Create a fragment buffer from the currently buffered children
2597
- _createBuffer: function(collectionView) {
2709
+ _createBuffer: function() {
2598
2710
  var elBuffer = document.createDocumentFragment();
2599
- _.each(collectionView._bufferedChildren, function(b) {
2711
+ _.each(this._bufferedChildren, function(b) {
2600
2712
  elBuffer.appendChild(b.el);
2601
2713
  });
2602
2714
  return elBuffer;
@@ -2657,7 +2769,7 @@
2657
2769
  if (this.isDestroyed) { return this; }
2658
2770
 
2659
2771
  this.triggerMethod('before:destroy:collection');
2660
- this.destroyChildren();
2772
+ this.destroyChildren({checkEmpty: false});
2661
2773
  this.triggerMethod('destroy:collection');
2662
2774
 
2663
2775
  return Marionette.View.prototype.destroy.apply(this, arguments);
@@ -2665,10 +2777,20 @@
2665
2777
 
2666
2778
  // Destroy the child views that this collection view
2667
2779
  // is holding on to, if any
2668
- destroyChildren: function() {
2780
+ destroyChildren: function(options) {
2781
+ var destroyOptions = options || {};
2782
+ var shouldCheckEmpty = true;
2669
2783
  var childViews = this.children.map(_.identity);
2784
+
2785
+ if (!_.isUndefined(destroyOptions.checkEmpty)) {
2786
+ shouldCheckEmpty = destroyOptions.checkEmpty;
2787
+ }
2788
+
2670
2789
  this.children.each(this.removeChildView, this);
2671
- this.checkEmpty();
2790
+
2791
+ if (shouldCheckEmpty) {
2792
+ this.checkEmpty();
2793
+ }
2672
2794
  return childViews;
2673
2795
  },
2674
2796
 
@@ -2712,6 +2834,11 @@
2712
2834
  return _.values(this.children._views);
2713
2835
  },
2714
2836
 
2837
+ _getViewAndNested: function(view) {
2838
+ // This will not fail on Backbone.View which does not have #_getNestedViews.
2839
+ return [view].concat(_.result(view, '_getNestedViews') || []);
2840
+ },
2841
+
2715
2842
  getViewComparator: function() {
2716
2843
  return this.getOption('viewComparator');
2717
2844
  }
@@ -2840,9 +2967,9 @@
2840
2967
  },
2841
2968
 
2842
2969
  // You might need to override this if you've overridden attachHtml
2843
- attachBuffer: function(compositeView) {
2970
+ attachBuffer: function(compositeView, buffer) {
2844
2971
  var $container = this.getChildViewContainer(compositeView);
2845
- $container.append(this._createBuffer(compositeView));
2972
+ $container.append(buffer);
2846
2973
  },
2847
2974
 
2848
2975
  // Internal method. Append a view to the end of the $el.
@@ -2864,7 +2991,7 @@
2864
2991
  // Internal method to ensure an `$childViewContainer` exists, for the
2865
2992
  // `attachHtml` method to use.
2866
2993
  getChildViewContainer: function(containerView, childView) {
2867
- if ('$childViewContainer' in containerView) {
2994
+ if (!!containerView.$childViewContainer) {
2868
2995
  return containerView.$childViewContainer;
2869
2996
  }
2870
2997
 
@@ -2898,7 +3025,7 @@
2898
3025
  // Internal method to reset the `$childViewContainer` on render
2899
3026
  resetChildViewContainer: function() {
2900
3027
  if (this.$childViewContainer) {
2901
- delete this.$childViewContainer;
3028
+ this.$childViewContainer = undefined;
2902
3029
  }
2903
3030
  }
2904
3031
  });
@@ -3408,7 +3535,7 @@
3408
3535
  this.submodules = {};
3409
3536
  _.extend(this, options);
3410
3537
  this._initChannel();
3411
- Marionette.Object.call(this, options);
3538
+ Marionette.Object.apply(this, arguments);
3412
3539
  },
3413
3540
 
3414
3541
  // Command execution, facilitated by Backbone.Wreqr.Commands