js_stack 1.10.0 → 1.11.1

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