marionette-rails 2.3.2 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 784f3ed30d496806b0e0eef14a7cf643152dbd24
|
4
|
+
data.tar.gz: 90e2455a564f630445b624cc51081fb35b143e07
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f6104cdf3edb687da3d167ea067fabe02a0bcdb67c9faff9ca917b8bc32b62311047d66db12e6b992dfa3cfa4bbaa2a3e7a40f4181c316c1fdb334bd957866db
|
7
|
+
data.tar.gz: 229f8eead03011880e83ccacd692c0be10da7df8265b92c78582af0aab09d819d0e31382bc53da8d0b38339f53333a4d401343958e12c51789bec7001c51b354
|
@@ -1,6 +1,6 @@
|
|
1
1
|
// MarionetteJS (Backbone.Marionette)
|
2
2
|
// ----------------------------------
|
3
|
-
// v2.
|
3
|
+
// v2.4.0
|
4
4
|
//
|
5
5
|
// Copyright (c)2015 Derick Bailey, Muted Solutions, LLC.
|
6
6
|
// Distributed under MIT license
|
@@ -38,9 +38,9 @@
|
|
38
38
|
/* istanbul ignore next */
|
39
39
|
// Backbone.BabySitter
|
40
40
|
// -------------------
|
41
|
-
// v0.1.
|
41
|
+
// v0.1.6
|
42
42
|
//
|
43
|
-
// Copyright (c)
|
43
|
+
// Copyright (c)2015 Derick Bailey, Muted Solutions, LLC.
|
44
44
|
// Distributed under MIT license
|
45
45
|
//
|
46
46
|
// http://github.com/marionettejs/backbone.babysitter
|
@@ -156,7 +156,7 @@
|
|
156
156
|
//
|
157
157
|
// Mix in methods from Underscore, for iteration, and other
|
158
158
|
// collection related features.
|
159
|
-
var methods = [ "forEach", "each", "map", "find", "detect", "filter", "select", "reject", "every", "all", "some", "any", "include", "contains", "invoke", "toArray", "first", "initial", "rest", "last", "without", "isEmpty", "pluck" ];
|
159
|
+
var methods = [ "forEach", "each", "map", "find", "detect", "filter", "select", "reject", "every", "all", "some", "any", "include", "contains", "invoke", "toArray", "first", "initial", "rest", "last", "without", "isEmpty", "pluck", "reduce" ];
|
160
160
|
_.each(methods, function(method) {
|
161
161
|
Container.prototype[method] = function() {
|
162
162
|
var views = _.values(this._views);
|
@@ -167,7 +167,7 @@
|
|
167
167
|
// return the public API
|
168
168
|
return Container;
|
169
169
|
}(Backbone, _);
|
170
|
-
Backbone.ChildViewContainer.VERSION = "0.1.
|
170
|
+
Backbone.ChildViewContainer.VERSION = "0.1.6";
|
171
171
|
Backbone.ChildViewContainer.noConflict = function() {
|
172
172
|
Backbone.ChildViewContainer = previousChildViewContainer;
|
173
173
|
return this;
|
@@ -490,13 +490,15 @@
|
|
490
490
|
})(Backbone, _);
|
491
491
|
|
492
492
|
var previousMarionette = root.Marionette;
|
493
|
+
var previousMn = root.Mn;
|
493
494
|
|
494
495
|
var Marionette = Backbone.Marionette = {};
|
495
496
|
|
496
|
-
Marionette.VERSION = '2.
|
497
|
+
Marionette.VERSION = '2.4.0';
|
497
498
|
|
498
499
|
Marionette.noConflict = function() {
|
499
500
|
root.Marionette = previousMarionette;
|
501
|
+
root.Mn = previousMn;
|
500
502
|
return this;
|
501
503
|
};
|
502
504
|
|
@@ -524,6 +526,11 @@
|
|
524
526
|
return Backbone.$.contains(document.documentElement, el);
|
525
527
|
};
|
526
528
|
|
529
|
+
// Merge `keys` from `options` onto `this`
|
530
|
+
Marionette.mergeOptions = function(options, keys) {
|
531
|
+
if (!options) { return; }
|
532
|
+
_.extend(this, _.pick(options, keys));
|
533
|
+
};
|
527
534
|
|
528
535
|
// Marionette.getOption
|
529
536
|
// --------------------
|
@@ -550,11 +557,7 @@
|
|
550
557
|
// undefined return a default value
|
551
558
|
Marionette._getValue = function(value, context, params) {
|
552
559
|
if (_.isFunction(value)) {
|
553
|
-
|
554
|
-
// to prevent `apply` from failing in ie8
|
555
|
-
params = params || [];
|
556
|
-
|
557
|
-
value = value.apply(context, params);
|
560
|
+
value = params ? value.apply(context, params) : value.call(context);
|
558
561
|
}
|
559
562
|
return value;
|
560
563
|
};
|
@@ -599,11 +602,21 @@
|
|
599
602
|
// allows for the use of the @ui. syntax within
|
600
603
|
// a given value for regions
|
601
604
|
// swaps the @ui with the associated selector
|
602
|
-
Marionette.normalizeUIValues = function(hash, ui) {
|
605
|
+
Marionette.normalizeUIValues = function(hash, ui, properties) {
|
603
606
|
_.each(hash, function(val, key) {
|
604
607
|
if (_.isString(val)) {
|
605
608
|
hash[key] = Marionette.normalizeUIString(val, ui);
|
606
609
|
}
|
610
|
+
else if (_.isObject(val) && _.isArray(properties)) {
|
611
|
+
_.extend(val, Marionette.normalizeUIValues(_.pick(val, properties), ui));
|
612
|
+
/* Value is an object, and we got an array of embedded property names to normalize. */
|
613
|
+
_.each(properties, function(property) {
|
614
|
+
var propertyVal = val[property];
|
615
|
+
if (_.isString(propertyVal)) {
|
616
|
+
val[property] = Marionette.normalizeUIString(propertyVal, ui);
|
617
|
+
}
|
618
|
+
});
|
619
|
+
}
|
607
620
|
});
|
608
621
|
return hash;
|
609
622
|
};
|
@@ -682,7 +695,7 @@
|
|
682
695
|
// trigger the event, if a trigger method exists
|
683
696
|
if (_.isFunction(context.trigger)) {
|
684
697
|
if (noEventArg + args.length > 1) {
|
685
|
-
context.trigger.apply(context, noEventArg ? args : [event].concat(_.
|
698
|
+
context.trigger.apply(context, noEventArg ? args : [event].concat(_.drop(args, 0)));
|
686
699
|
} else {
|
687
700
|
context.trigger(event);
|
688
701
|
}
|
@@ -984,6 +997,9 @@
|
|
984
997
|
// methods if the method exists
|
985
998
|
triggerMethod: Marionette.triggerMethod,
|
986
999
|
|
1000
|
+
// A handy way to merge options onto the instance
|
1001
|
+
mergeOptions: Marionette.mergeOptions,
|
1002
|
+
|
987
1003
|
// Proxy `getOption` to enable getting options from this or this.options by name.
|
988
1004
|
getOption: Marionette.proxyGetOption
|
989
1005
|
|
@@ -1015,12 +1031,17 @@
|
|
1015
1031
|
this.triggerMethod('before:destroy');
|
1016
1032
|
this.triggerMethod('destroy');
|
1017
1033
|
this.stopListening();
|
1034
|
+
|
1035
|
+
return this;
|
1018
1036
|
},
|
1019
1037
|
|
1020
1038
|
// Import the `triggerMethod` to trigger events with corresponding
|
1021
1039
|
// methods if the method exists
|
1022
1040
|
triggerMethod: Marionette.triggerMethod,
|
1023
1041
|
|
1042
|
+
// A handy way to merge options onto the instance
|
1043
|
+
mergeOptions: Marionette.mergeOptions,
|
1044
|
+
|
1024
1045
|
// Proxy `getOption` to enable getting options from this or this.options by name.
|
1025
1046
|
getOption: Marionette.proxyGetOption,
|
1026
1047
|
|
@@ -1235,20 +1256,28 @@
|
|
1235
1256
|
|
1236
1257
|
// Destroy the current view, if there is one. If there is no
|
1237
1258
|
// current view, it does nothing and returns immediately.
|
1238
|
-
empty: function() {
|
1259
|
+
empty: function(options) {
|
1239
1260
|
var view = this.currentView;
|
1240
1261
|
|
1262
|
+
var preventDestroy = Marionette._getValue(options, 'preventDestroy', this);
|
1241
1263
|
// If there is no view in the region
|
1242
1264
|
// we should not remove anything
|
1243
1265
|
if (!view) { return; }
|
1244
1266
|
|
1245
1267
|
view.off('destroy', this.empty, this);
|
1246
1268
|
this.triggerMethod('before:empty', view);
|
1247
|
-
|
1269
|
+
if (!preventDestroy) {
|
1270
|
+
this._destroyView();
|
1271
|
+
}
|
1248
1272
|
this.triggerMethod('empty', view);
|
1249
1273
|
|
1250
1274
|
// Remove region pointer to the currentView
|
1251
1275
|
delete this.currentView;
|
1276
|
+
|
1277
|
+
if (preventDestroy) {
|
1278
|
+
this.$el.contents().detach();
|
1279
|
+
}
|
1280
|
+
|
1252
1281
|
return this;
|
1253
1282
|
},
|
1254
1283
|
|
@@ -1371,6 +1400,7 @@
|
|
1371
1400
|
Marionette.RegionManager = Marionette.Controller.extend({
|
1372
1401
|
constructor: function(options) {
|
1373
1402
|
this._regions = {};
|
1403
|
+
this.length = 0;
|
1374
1404
|
|
1375
1405
|
Marionette.Controller.call(this, options);
|
1376
1406
|
|
@@ -1464,8 +1494,11 @@
|
|
1464
1494
|
|
1465
1495
|
// internal method to store regions
|
1466
1496
|
_store: function(name, region) {
|
1497
|
+
if (!this._regions[name]) {
|
1498
|
+
this.length++;
|
1499
|
+
}
|
1500
|
+
|
1467
1501
|
this._regions[name] = region;
|
1468
|
-
this._setLength();
|
1469
1502
|
},
|
1470
1503
|
|
1471
1504
|
// internal method to remove a region
|
@@ -1476,13 +1509,8 @@
|
|
1476
1509
|
|
1477
1510
|
delete region._parent;
|
1478
1511
|
delete this._regions[name];
|
1479
|
-
this.
|
1512
|
+
this.length--;
|
1480
1513
|
this.triggerMethod('remove:region', name, region);
|
1481
|
-
},
|
1482
|
-
|
1483
|
-
// set the number of regions current held
|
1484
|
-
_setLength: function() {
|
1485
|
-
this.length = _.size(this._regions);
|
1486
1514
|
}
|
1487
1515
|
});
|
1488
1516
|
|
@@ -1507,7 +1535,7 @@
|
|
1507
1535
|
// Get the specified template by id. Either
|
1508
1536
|
// retrieves the cached version, or loads it
|
1509
1537
|
// from the DOM.
|
1510
|
-
get: function(templateId) {
|
1538
|
+
get: function(templateId, options) {
|
1511
1539
|
var cachedTemplate = this.templateCaches[templateId];
|
1512
1540
|
|
1513
1541
|
if (!cachedTemplate) {
|
@@ -1515,7 +1543,7 @@
|
|
1515
1543
|
this.templateCaches[templateId] = cachedTemplate;
|
1516
1544
|
}
|
1517
1545
|
|
1518
|
-
return cachedTemplate.load();
|
1546
|
+
return cachedTemplate.load(options);
|
1519
1547
|
},
|
1520
1548
|
|
1521
1549
|
// Clear templates from the cache. If no arguments
|
@@ -1546,15 +1574,15 @@
|
|
1546
1574
|
_.extend(Marionette.TemplateCache.prototype, {
|
1547
1575
|
|
1548
1576
|
// Internal method to load the template
|
1549
|
-
load: function() {
|
1577
|
+
load: function(options) {
|
1550
1578
|
// Guard clause to prevent loading this template more than once
|
1551
1579
|
if (this.compiledTemplate) {
|
1552
1580
|
return this.compiledTemplate;
|
1553
1581
|
}
|
1554
1582
|
|
1555
1583
|
// Load the template and compile it
|
1556
|
-
var template = this.loadTemplate(this.templateId);
|
1557
|
-
this.compiledTemplate = this.compileTemplate(template);
|
1584
|
+
var template = this.loadTemplate(this.templateId, options);
|
1585
|
+
this.compiledTemplate = this.compileTemplate(template, options);
|
1558
1586
|
|
1559
1587
|
return this.compiledTemplate;
|
1560
1588
|
},
|
@@ -1564,7 +1592,7 @@
|
|
1564
1592
|
// For asynchronous loading with AMD/RequireJS, consider
|
1565
1593
|
// using a template-loader plugin as described here:
|
1566
1594
|
// https://github.com/marionettejs/backbone.marionette/wiki/Using-marionette-with-requirejs
|
1567
|
-
loadTemplate: function(templateId) {
|
1595
|
+
loadTemplate: function(templateId, options) {
|
1568
1596
|
var template = Backbone.$(templateId).html();
|
1569
1597
|
|
1570
1598
|
if (!template || template.length === 0) {
|
@@ -1581,8 +1609,8 @@
|
|
1581
1609
|
// this method if you do not need to pre-compile a template
|
1582
1610
|
// (JST / RequireJS for example) or if you want to change
|
1583
1611
|
// the template engine used (Handebars, etc).
|
1584
|
-
compileTemplate: function(rawTemplate) {
|
1585
|
-
return _.template(rawTemplate);
|
1612
|
+
compileTemplate: function(rawTemplate, options) {
|
1613
|
+
return _.template(rawTemplate, options);
|
1586
1614
|
}
|
1587
1615
|
});
|
1588
1616
|
|
@@ -1633,10 +1661,9 @@
|
|
1633
1661
|
|
1634
1662
|
this._behaviors = Marionette.Behaviors(this);
|
1635
1663
|
|
1636
|
-
Backbone.View.
|
1664
|
+
Backbone.View.call(this, this.options);
|
1637
1665
|
|
1638
1666
|
Marionette.MonitorDOMRefresh(this);
|
1639
|
-
this.on('show', this.onShowCalled);
|
1640
1667
|
},
|
1641
1668
|
|
1642
1669
|
// Get the template for this view
|
@@ -1674,10 +1701,10 @@
|
|
1674
1701
|
|
1675
1702
|
// normalize the values of passed hash with the views `ui` selectors.
|
1676
1703
|
// `{foo: "@ui.bar"}`
|
1677
|
-
normalizeUIValues: function(hash) {
|
1704
|
+
normalizeUIValues: function(hash, properties) {
|
1678
1705
|
var ui = _.result(this, 'ui');
|
1679
1706
|
var uiBindings = _.result(this, '_uiBindings');
|
1680
|
-
return Marionette.normalizeUIValues(hash, uiBindings || ui);
|
1707
|
+
return Marionette.normalizeUIValues(hash, uiBindings || ui, properties);
|
1681
1708
|
},
|
1682
1709
|
|
1683
1710
|
// Configure `triggers` to forward DOM events to view
|
@@ -1748,9 +1775,6 @@
|
|
1748
1775
|
return this;
|
1749
1776
|
},
|
1750
1777
|
|
1751
|
-
// Internal method, handles the `show` event.
|
1752
|
-
onShowCalled: function() {},
|
1753
|
-
|
1754
1778
|
// Internal helper method to verify whether the view hasn't been destroyed
|
1755
1779
|
_ensureViewIsIntact: function() {
|
1756
1780
|
if (this.isDestroyed) {
|
@@ -1766,7 +1790,7 @@
|
|
1766
1790
|
// for you. You can specify an `onDestroy` method in your view to
|
1767
1791
|
// add custom code that is called after the view is destroyed.
|
1768
1792
|
destroy: function() {
|
1769
|
-
if (this.isDestroyed) { return; }
|
1793
|
+
if (this.isDestroyed) { return this; }
|
1770
1794
|
|
1771
1795
|
var args = _.toArray(arguments);
|
1772
1796
|
|
@@ -1781,6 +1805,8 @@
|
|
1781
1805
|
// unbind UI elements
|
1782
1806
|
this.unbindUIElements();
|
1783
1807
|
|
1808
|
+
this.isRendered = false;
|
1809
|
+
|
1784
1810
|
// remove the view from the DOM
|
1785
1811
|
this.remove();
|
1786
1812
|
|
@@ -1887,15 +1913,42 @@
|
|
1887
1913
|
// import the `triggerMethod` to trigger events with corresponding
|
1888
1914
|
// methods if the method exists
|
1889
1915
|
triggerMethod: function() {
|
1916
|
+
var ret = Marionette._triggerMethod(this, arguments);
|
1917
|
+
|
1918
|
+
this._triggerEventOnBehaviors(arguments);
|
1919
|
+
this._triggerEventOnParentLayout(arguments[0], _.rest(arguments));
|
1920
|
+
|
1921
|
+
return ret;
|
1922
|
+
},
|
1923
|
+
|
1924
|
+
_triggerEventOnBehaviors: function(args) {
|
1890
1925
|
var triggerMethod = Marionette._triggerMethod;
|
1891
|
-
var ret = triggerMethod(this, arguments);
|
1892
1926
|
var behaviors = this._behaviors;
|
1893
1927
|
// Use good ol' for as this is a very hot function
|
1894
1928
|
for (var i = 0, length = behaviors && behaviors.length; i < length; i++) {
|
1895
|
-
triggerMethod(behaviors[i],
|
1929
|
+
triggerMethod(behaviors[i], args);
|
1896
1930
|
}
|
1931
|
+
},
|
1897
1932
|
|
1898
|
-
|
1933
|
+
_triggerEventOnParentLayout: function(eventName, args) {
|
1934
|
+
var layoutView = this._parentLayoutView();
|
1935
|
+
if (!layoutView) {
|
1936
|
+
return;
|
1937
|
+
}
|
1938
|
+
|
1939
|
+
// invoke triggerMethod on parent view
|
1940
|
+
var eventPrefix = Marionette.getOption(layoutView, 'childViewEventPrefix');
|
1941
|
+
var prefixedEventName = eventPrefix + ':' + eventName;
|
1942
|
+
|
1943
|
+
Marionette._triggerMethod(layoutView, [prefixedEventName, this].concat(args));
|
1944
|
+
|
1945
|
+
// call the parent view's childEvents handler
|
1946
|
+
var childEvents = Marionette.getOption(layoutView, 'childEvents');
|
1947
|
+
var normalizedChildEvents = layoutView.normalizeMethods(childEvents);
|
1948
|
+
|
1949
|
+
if (!!normalizedChildEvents && _.isFunction(normalizedChildEvents[eventName])) {
|
1950
|
+
normalizedChildEvents[eventName].apply(layoutView, [this].concat(args));
|
1951
|
+
}
|
1899
1952
|
},
|
1900
1953
|
|
1901
1954
|
// This method returns any views that are immediate
|
@@ -1916,10 +1969,35 @@
|
|
1916
1969
|
}, children);
|
1917
1970
|
},
|
1918
1971
|
|
1972
|
+
// Internal utility for building an ancestor
|
1973
|
+
// view tree list.
|
1974
|
+
_getAncestors: function() {
|
1975
|
+
var ancestors = [];
|
1976
|
+
var parent = this._parent;
|
1977
|
+
|
1978
|
+
while (parent) {
|
1979
|
+
ancestors.push(parent);
|
1980
|
+
parent = parent._parent;
|
1981
|
+
}
|
1982
|
+
|
1983
|
+
return ancestors;
|
1984
|
+
},
|
1985
|
+
|
1986
|
+
// Returns the containing parent view.
|
1987
|
+
_parentLayoutView: function() {
|
1988
|
+
var ancestors = this._getAncestors();
|
1989
|
+
return _.find(ancestors, function(parent) {
|
1990
|
+
return parent instanceof Marionette.LayoutView;
|
1991
|
+
});
|
1992
|
+
},
|
1993
|
+
|
1919
1994
|
// Imports the "normalizeMethods" to transform hashes of
|
1920
1995
|
// events=>function references/names to a hash of events=>function references
|
1921
1996
|
normalizeMethods: Marionette.normalizeMethods,
|
1922
1997
|
|
1998
|
+
// A handy way to merge passed-in options onto the instance
|
1999
|
+
mergeOptions: Marionette.mergeOptions,
|
2000
|
+
|
1923
2001
|
// Proxy `getOption` to enable getting options from this or this.options by name.
|
1924
2002
|
getOption: Marionette.proxyGetOption,
|
1925
2003
|
|
@@ -1986,6 +2064,7 @@
|
|
1986
2064
|
this.triggerMethod('before:render', this);
|
1987
2065
|
|
1988
2066
|
this._renderTemplate();
|
2067
|
+
this.isRendered = true;
|
1989
2068
|
this.bindUIElements();
|
1990
2069
|
|
1991
2070
|
this.triggerMethod('render', this);
|
@@ -2013,8 +2092,7 @@
|
|
2013
2092
|
}
|
2014
2093
|
|
2015
2094
|
// Add in entity data and template helpers
|
2016
|
-
var data = this.serializeData();
|
2017
|
-
data = this.mixinTemplateHelpers(data);
|
2095
|
+
var data = this.mixinTemplateHelpers(this.serializeData());
|
2018
2096
|
|
2019
2097
|
// Render and add to el
|
2020
2098
|
var html = Marionette.Renderer.render(template, data, this);
|
@@ -2055,21 +2133,25 @@
|
|
2055
2133
|
// that are forwarded through the collectionview
|
2056
2134
|
childViewEventPrefix: 'childview',
|
2057
2135
|
|
2136
|
+
// flag for maintaining the sorted order of the collection
|
2137
|
+
sort: true,
|
2138
|
+
|
2058
2139
|
// constructor
|
2059
2140
|
// option to pass `{sort: false}` to prevent the `CollectionView` from
|
2060
2141
|
// maintaining the sorted order of the collection.
|
2061
2142
|
// This will fallback onto appending childView's to the end.
|
2143
|
+
//
|
2144
|
+
// option to pass `{comparator: compFunction()}` to allow the `CollectionView`
|
2145
|
+
// to use a custom sort order for the collection.
|
2062
2146
|
constructor: function(options){
|
2063
|
-
var initOptions = options || {};
|
2064
|
-
if (_.isUndefined(this.sort)){
|
2065
|
-
this.sort = _.isUndefined(initOptions.sort) ? true : initOptions.sort;
|
2066
|
-
}
|
2067
2147
|
|
2068
2148
|
this.once('render', this._initialEvents);
|
2069
2149
|
this._initChildViewStorage();
|
2070
2150
|
|
2071
2151
|
Marionette.View.apply(this, arguments);
|
2072
2152
|
|
2153
|
+
this.on('show', this._onShowCalled);
|
2154
|
+
|
2073
2155
|
this.initRenderBuffer();
|
2074
2156
|
},
|
2075
2157
|
|
@@ -2077,7 +2159,6 @@
|
|
2077
2159
|
// it's much more performant to insert elements into a document
|
2078
2160
|
// fragment and then insert that document fragment into the page
|
2079
2161
|
initRenderBuffer: function() {
|
2080
|
-
this.elBuffer = document.createDocumentFragment();
|
2081
2162
|
this._bufferedChildren = [];
|
2082
2163
|
},
|
2083
2164
|
|
@@ -2089,7 +2170,9 @@
|
|
2089
2170
|
endBuffering: function() {
|
2090
2171
|
this.isBuffering = false;
|
2091
2172
|
this._triggerBeforeShowBufferedChildren();
|
2092
|
-
|
2173
|
+
|
2174
|
+
this.attachBuffer(this);
|
2175
|
+
|
2093
2176
|
this._triggerShowBufferedChildren();
|
2094
2177
|
this.initRenderBuffer();
|
2095
2178
|
},
|
@@ -2122,18 +2205,26 @@
|
|
2122
2205
|
this.listenTo(this.collection, 'remove', this._onCollectionRemove);
|
2123
2206
|
this.listenTo(this.collection, 'reset', this.render);
|
2124
2207
|
|
2125
|
-
if (this.sort) {
|
2208
|
+
if (this.getOption('sort')) {
|
2126
2209
|
this.listenTo(this.collection, 'sort', this._sortViews);
|
2127
2210
|
}
|
2128
2211
|
}
|
2129
2212
|
},
|
2130
2213
|
|
2131
2214
|
// Handle a child added to the collection
|
2132
|
-
_onCollectionAdd: function(child) {
|
2133
|
-
|
2134
|
-
|
2135
|
-
|
2136
|
-
|
2215
|
+
_onCollectionAdd: function(child, collection, opts) {
|
2216
|
+
var index;
|
2217
|
+
if (opts.at !== undefined) {
|
2218
|
+
index = opts.at;
|
2219
|
+
} else {
|
2220
|
+
index = _.indexOf(this._filteredSortedModels(), child);
|
2221
|
+
}
|
2222
|
+
|
2223
|
+
if (this._shouldAddChild(child, index)) {
|
2224
|
+
this.destroyEmptyView();
|
2225
|
+
var ChildView = this.getChildView(child);
|
2226
|
+
this.addChild(child, ChildView, index);
|
2227
|
+
}
|
2137
2228
|
},
|
2138
2229
|
|
2139
2230
|
// get the child view by model it holds, and remove it
|
@@ -2143,8 +2234,7 @@
|
|
2143
2234
|
this.checkEmpty();
|
2144
2235
|
},
|
2145
2236
|
|
2146
|
-
|
2147
|
-
onShowCalled: function() {
|
2237
|
+
_onShowCalled: function() {
|
2148
2238
|
this.children.each(_.partial(this._triggerMethodOnChild, 'show'));
|
2149
2239
|
},
|
2150
2240
|
|
@@ -2155,23 +2245,60 @@
|
|
2155
2245
|
this._ensureViewIsIntact();
|
2156
2246
|
this.triggerMethod('before:render', this);
|
2157
2247
|
this._renderChildren();
|
2248
|
+
this.isRendered = true;
|
2158
2249
|
this.triggerMethod('render', this);
|
2159
2250
|
return this;
|
2160
2251
|
},
|
2161
2252
|
|
2253
|
+
// Reorder DOM after sorting. When your element's rendering
|
2254
|
+
// do not use their index, you can pass reorderOnSort: true
|
2255
|
+
// to only reorder the DOM after a sort instead of rendering
|
2256
|
+
// all the collectionView
|
2257
|
+
reorder: function () {
|
2258
|
+
var children = this.children;
|
2259
|
+
var models = this._filteredSortedModels();
|
2260
|
+
var modelsChanged = _.find(models, function (model) {
|
2261
|
+
return !children.findByModel(model);
|
2262
|
+
});
|
2263
|
+
|
2264
|
+
// If the models we're displaying have changed due to filtering
|
2265
|
+
// We need to add and/or remove child views
|
2266
|
+
// So render as normal
|
2267
|
+
if (modelsChanged) {
|
2268
|
+
this.render();
|
2269
|
+
} else {
|
2270
|
+
// get the DOM nodes in the same order as the models
|
2271
|
+
var els = _.map(models, function (model) {
|
2272
|
+
return children.findByModel(model).el;
|
2273
|
+
});
|
2274
|
+
|
2275
|
+
// since append moves elements that are already in the DOM,
|
2276
|
+
// appending the elements will effectively reorder them
|
2277
|
+
this.triggerMethod('before:reorder');
|
2278
|
+
this.$el.append(els);
|
2279
|
+
this.triggerMethod('reorder');
|
2280
|
+
}
|
2281
|
+
},
|
2282
|
+
|
2162
2283
|
// Render view after sorting. Override this method to
|
2163
2284
|
// change how the view renders after a `sort` on the collection.
|
2164
2285
|
// An example of this would be to only `renderChildren` in a `CompositeView`
|
2165
2286
|
// rather than the full view.
|
2166
2287
|
resortView: function() {
|
2167
|
-
|
2288
|
+
if (Marionette.getOption(this, 'reorderOnSort')) {
|
2289
|
+
this.reorder();
|
2290
|
+
} else {
|
2291
|
+
this.render();
|
2292
|
+
}
|
2168
2293
|
},
|
2169
2294
|
|
2170
2295
|
// Internal method. This checks for any changes in the order of the collection.
|
2171
2296
|
// If the index of any view doesn't match, it will render.
|
2172
2297
|
_sortViews: function() {
|
2298
|
+
var models = this._filteredSortedModels();
|
2299
|
+
|
2173
2300
|
// check for any changes in sort order of views
|
2174
|
-
var orderChanged =
|
2301
|
+
var orderChanged = _.find(models, function(item, index){
|
2175
2302
|
var view = this.children.findByModel(item);
|
2176
2303
|
return !view || view._index !== index;
|
2177
2304
|
}, this);
|
@@ -2199,18 +2326,51 @@
|
|
2199
2326
|
this.showCollection();
|
2200
2327
|
this.endBuffering();
|
2201
2328
|
this.triggerMethod('render:collection', this);
|
2329
|
+
|
2330
|
+
// If we have shown children and none have passed the filter, show the empty view
|
2331
|
+
if (this.children.isEmpty()) {
|
2332
|
+
this.showEmptyView();
|
2333
|
+
}
|
2202
2334
|
}
|
2203
2335
|
},
|
2204
2336
|
|
2205
2337
|
// Internal method to loop through collection and show each child view.
|
2206
2338
|
showCollection: function() {
|
2207
2339
|
var ChildView;
|
2208
|
-
|
2340
|
+
|
2341
|
+
var models = this._filteredSortedModels();
|
2342
|
+
|
2343
|
+
_.each(models, function(child, index) {
|
2209
2344
|
ChildView = this.getChildView(child);
|
2210
2345
|
this.addChild(child, ChildView, index);
|
2211
2346
|
}, this);
|
2212
2347
|
},
|
2213
2348
|
|
2349
|
+
// Allow the collection to be sorted by a custom view comparator
|
2350
|
+
_filteredSortedModels: function() {
|
2351
|
+
var models;
|
2352
|
+
var viewComparator = this.getViewComparator();
|
2353
|
+
|
2354
|
+
if (viewComparator) {
|
2355
|
+
if (_.isString(viewComparator) || viewComparator.length === 1) {
|
2356
|
+
models = this.collection.sortBy(viewComparator, this);
|
2357
|
+
} else {
|
2358
|
+
models = _.clone(this.collection.models).sort(_.bind(viewComparator, this));
|
2359
|
+
}
|
2360
|
+
} else {
|
2361
|
+
models = this.collection.models;
|
2362
|
+
}
|
2363
|
+
|
2364
|
+
// Filter after sorting in case the filter uses the index
|
2365
|
+
if (this.getOption('filter')) {
|
2366
|
+
models = _.filter(models, function (model, index) {
|
2367
|
+
return this._shouldAddChild(model, index);
|
2368
|
+
}, this);
|
2369
|
+
}
|
2370
|
+
|
2371
|
+
return models;
|
2372
|
+
},
|
2373
|
+
|
2214
2374
|
// Internal method to show an empty view in place of
|
2215
2375
|
// a collection of child views, when the collection is empty
|
2216
2376
|
showEmptyView: function() {
|
@@ -2329,7 +2489,7 @@
|
|
2329
2489
|
// Internal method. This decrements or increments the indices of views after the
|
2330
2490
|
// added/removed view to keep in sync with the collection.
|
2331
2491
|
_updateIndices: function(view, increment, index) {
|
2332
|
-
if (!this.sort) {
|
2492
|
+
if (!this.getOption('sort')) {
|
2333
2493
|
return;
|
2334
2494
|
}
|
2335
2495
|
|
@@ -2355,6 +2515,12 @@
|
|
2355
2515
|
|
2356
2516
|
this.triggerMethod('before:add:child', view);
|
2357
2517
|
|
2518
|
+
// trigger the 'before:show' event on `view` if the collection view
|
2519
|
+
// has already been shown
|
2520
|
+
if (this._isShown && !this.isBuffering) {
|
2521
|
+
Marionette.triggerMethodOn(view, 'before:show');
|
2522
|
+
}
|
2523
|
+
|
2358
2524
|
// Store the child view itself so we can properly
|
2359
2525
|
// remove and/or destroy it later
|
2360
2526
|
this.children.add(view);
|
@@ -2417,8 +2583,17 @@
|
|
2417
2583
|
},
|
2418
2584
|
|
2419
2585
|
// You might need to override this if you've overridden attachHtml
|
2420
|
-
attachBuffer: function(collectionView
|
2421
|
-
collectionView.$el.append(
|
2586
|
+
attachBuffer: function(collectionView) {
|
2587
|
+
collectionView.$el.append(this._createBuffer(collectionView));
|
2588
|
+
},
|
2589
|
+
|
2590
|
+
// Create a fragment buffer from the currently buffered children
|
2591
|
+
_createBuffer: function(collectionView) {
|
2592
|
+
var elBuffer = document.createDocumentFragment();
|
2593
|
+
_.each(collectionView._bufferedChildren, function(b) {
|
2594
|
+
elBuffer.appendChild(b.el);
|
2595
|
+
});
|
2596
|
+
return elBuffer;
|
2422
2597
|
},
|
2423
2598
|
|
2424
2599
|
// Append the HTML to the collection's `el`.
|
@@ -2429,8 +2604,7 @@
|
|
2429
2604
|
// buffering happens on reset events and initial renders
|
2430
2605
|
// in order to reduce the number of inserts into the
|
2431
2606
|
// document, which are expensive.
|
2432
|
-
collectionView.
|
2433
|
-
collectionView._bufferedChildren.push(childView);
|
2607
|
+
collectionView._bufferedChildren.splice(index, 0, childView);
|
2434
2608
|
}
|
2435
2609
|
else {
|
2436
2610
|
// If we've already rendered the main collection, append
|
@@ -2446,7 +2620,7 @@
|
|
2446
2620
|
// the correct position.
|
2447
2621
|
_insertBefore: function(childView, index) {
|
2448
2622
|
var currentView;
|
2449
|
-
var findPosition = this.sort && (index < this.children.length - 1);
|
2623
|
+
var findPosition = this.getOption('sort') && (index < this.children.length - 1);
|
2450
2624
|
if (findPosition) {
|
2451
2625
|
// Find the view after this one
|
2452
2626
|
currentView = this.children.find(function (view) {
|
@@ -2475,7 +2649,7 @@
|
|
2475
2649
|
|
2476
2650
|
// Handle cleanup and other destroying needs for the collection of views
|
2477
2651
|
destroy: function() {
|
2478
|
-
if (this.isDestroyed) { return; }
|
2652
|
+
if (this.isDestroyed) { return this; }
|
2479
2653
|
|
2480
2654
|
this.triggerMethod('before:destroy:collection');
|
2481
2655
|
this.destroyChildren();
|
@@ -2493,6 +2667,18 @@
|
|
2493
2667
|
return childViews;
|
2494
2668
|
},
|
2495
2669
|
|
2670
|
+
// Return true if the given child should be shown
|
2671
|
+
// Return false otherwise
|
2672
|
+
// The filter will be passed (child, index, collection)
|
2673
|
+
// Where
|
2674
|
+
// 'child' is the given model
|
2675
|
+
// 'index' is the index of that model in the collection
|
2676
|
+
// 'collection' is the collection referenced by this CollectionView
|
2677
|
+
_shouldAddChild: function (child, index) {
|
2678
|
+
var filter = this.getOption('filter');
|
2679
|
+
return !_.isFunction(filter) || filter.call(this, child, index, this.collection);
|
2680
|
+
},
|
2681
|
+
|
2496
2682
|
// Set up the child view event forwarding. Uses a "childview:"
|
2497
2683
|
// prefix in front of all forwarded events.
|
2498
2684
|
proxyChildEvents: function(view) {
|
@@ -2514,11 +2700,15 @@
|
|
2514
2700
|
}
|
2515
2701
|
|
2516
2702
|
this.triggerMethod.apply(this, args);
|
2517
|
-
}
|
2703
|
+
});
|
2518
2704
|
},
|
2519
2705
|
|
2520
2706
|
_getImmediateChildren: function() {
|
2521
2707
|
return _.values(this.children._views);
|
2708
|
+
},
|
2709
|
+
|
2710
|
+
getViewComparator: function() {
|
2711
|
+
return this.getOption('viewComparator');
|
2522
2712
|
}
|
2523
2713
|
});
|
2524
2714
|
|
@@ -2554,7 +2744,7 @@
|
|
2554
2744
|
this.listenTo(this.collection, 'remove', this._onCollectionRemove);
|
2555
2745
|
this.listenTo(this.collection, 'reset', this._renderChildren);
|
2556
2746
|
|
2557
|
-
if (this.sort) {
|
2747
|
+
if (this.getOption('sort')) {
|
2558
2748
|
this.listenTo(this.collection, 'sort', this._sortViews);
|
2559
2749
|
}
|
2560
2750
|
}
|
@@ -2586,7 +2776,7 @@
|
|
2586
2776
|
// Renders the model and the collection.
|
2587
2777
|
render: function() {
|
2588
2778
|
this._ensureViewIsIntact();
|
2589
|
-
this.
|
2779
|
+
this._isRendering = true;
|
2590
2780
|
this.resetChildViewContainer();
|
2591
2781
|
|
2592
2782
|
this.triggerMethod('before:render', this);
|
@@ -2594,12 +2784,14 @@
|
|
2594
2784
|
this._renderTemplate();
|
2595
2785
|
this._renderChildren();
|
2596
2786
|
|
2787
|
+
this._isRendering = false;
|
2788
|
+
this.isRendered = true;
|
2597
2789
|
this.triggerMethod('render', this);
|
2598
2790
|
return this;
|
2599
2791
|
},
|
2600
2792
|
|
2601
2793
|
_renderChildren: function() {
|
2602
|
-
if (this.isRendered) {
|
2794
|
+
if (this.isRendered || this._isRendering) {
|
2603
2795
|
Marionette.CollectionView.prototype._renderChildren.call(this);
|
2604
2796
|
}
|
2605
2797
|
},
|
@@ -2643,9 +2835,9 @@
|
|
2643
2835
|
},
|
2644
2836
|
|
2645
2837
|
// You might need to override this if you've overridden attachHtml
|
2646
|
-
attachBuffer: function(compositeView
|
2838
|
+
attachBuffer: function(compositeView) {
|
2647
2839
|
var $container = this.getChildViewContainer(compositeView);
|
2648
|
-
$container.append(
|
2840
|
+
$container.append(this._createBuffer(compositeView));
|
2649
2841
|
},
|
2650
2842
|
|
2651
2843
|
// Internal method. Append a view to the end of the $el.
|
@@ -2710,6 +2902,14 @@
|
|
2710
2902
|
Marionette.LayoutView = Marionette.ItemView.extend({
|
2711
2903
|
regionClass: Marionette.Region,
|
2712
2904
|
|
2905
|
+
options: {
|
2906
|
+
destroyImmediate: false
|
2907
|
+
},
|
2908
|
+
|
2909
|
+
// used as the prefix for child view events
|
2910
|
+
// that are forwarded through the layoutview
|
2911
|
+
childViewEventPrefix: 'childview',
|
2912
|
+
|
2713
2913
|
// Ensure the regions are available when the `initialize` method
|
2714
2914
|
// is called.
|
2715
2915
|
constructor: function(options) {
|
@@ -2744,11 +2944,23 @@
|
|
2744
2944
|
// Handle destroying regions, and then destroy the view itself.
|
2745
2945
|
destroy: function() {
|
2746
2946
|
if (this.isDestroyed) { return this; }
|
2747
|
-
|
2947
|
+
// #2134: remove parent element before destroying the child views, so
|
2948
|
+
// removing the child views doesn't retrigger repaints
|
2949
|
+
if(this.getOption('destroyImmediate') === true) {
|
2950
|
+
this.$el.remove();
|
2951
|
+
}
|
2748
2952
|
this.regionManager.destroy();
|
2749
2953
|
return Marionette.ItemView.prototype.destroy.apply(this, arguments);
|
2750
2954
|
},
|
2751
2955
|
|
2956
|
+
showChildView: function(regionName, view) {
|
2957
|
+
return this.getRegion(regionName).show(view);
|
2958
|
+
},
|
2959
|
+
|
2960
|
+
getChildView: function(regionName) {
|
2961
|
+
return this.getRegion(regionName).currentView;
|
2962
|
+
},
|
2963
|
+
|
2752
2964
|
// Add a single region, by name, to the layoutView
|
2753
2965
|
addRegion: function(name, definition) {
|
2754
2966
|
var regions = {};
|
@@ -2808,7 +3020,7 @@
|
|
2808
3020
|
|
2809
3021
|
// Normalize region selectors hash to allow
|
2810
3022
|
// a user to use the @ui. syntax.
|
2811
|
-
regions = this.normalizeUIValues(regions);
|
3023
|
+
regions = this.normalizeUIValues(regions, ['selector', 'el']);
|
2812
3024
|
|
2813
3025
|
this.addRegions(regions);
|
2814
3026
|
},
|
@@ -2877,6 +3089,12 @@
|
|
2877
3089
|
this.view = view;
|
2878
3090
|
this.defaults = _.result(this, 'defaults') || {};
|
2879
3091
|
this.options = _.extend({}, this.defaults, options);
|
3092
|
+
// Construct an internal UI hash using
|
3093
|
+
// the views UI hash and then the behaviors UI hash.
|
3094
|
+
// This allows the user to use UI hash elements
|
3095
|
+
// defined in the parent view as well as those
|
3096
|
+
// defined in the given behavior.
|
3097
|
+
this.ui = _.extend({}, _.result(view, 'ui'), _.result(this, 'ui'));
|
2880
3098
|
|
2881
3099
|
Marionette.Object.apply(this, arguments);
|
2882
3100
|
},
|
@@ -2892,6 +3110,8 @@
|
|
2892
3110
|
// Overrides Object#destroy to prevent additional events from being triggered.
|
2893
3111
|
destroy: function() {
|
2894
3112
|
this.stopListening();
|
3113
|
+
|
3114
|
+
return this;
|
2895
3115
|
},
|
2896
3116
|
|
2897
3117
|
proxyViewProperties: function (view) {
|
@@ -2939,23 +3159,15 @@
|
|
2939
3159
|
|
2940
3160
|
behaviorEvents: function(behaviorEvents, behaviors) {
|
2941
3161
|
var _behaviorsEvents = {};
|
2942
|
-
var viewUI = this._uiBindings || _.result(this, 'ui');
|
2943
3162
|
|
2944
3163
|
_.each(behaviors, function(b, i) {
|
2945
3164
|
var _events = {};
|
2946
3165
|
var behaviorEvents = _.clone(_.result(b, 'events')) || {};
|
2947
|
-
var behaviorUI = b._uiBindings || _.result(b, 'ui');
|
2948
3166
|
|
2949
|
-
// Construct an internal UI hash first using
|
2950
|
-
// the views UI hash and then the behaviors UI hash.
|
2951
|
-
// This allows the user to use UI hash elements
|
2952
|
-
// defined in the parent view as well as those
|
2953
|
-
// defined in the given behavior.
|
2954
|
-
var ui = _.extend({}, viewUI, behaviorUI);
|
2955
3167
|
|
2956
3168
|
// Normalize behavior events hash to allow
|
2957
3169
|
// a user to use the @ui. syntax.
|
2958
|
-
behaviorEvents = Marionette.normalizeUIKeys(behaviorEvents,
|
3170
|
+
behaviorEvents = Marionette.normalizeUIKeys(behaviorEvents, getBehaviorsUI(b));
|
2959
3171
|
|
2960
3172
|
var j = 0;
|
2961
3173
|
_.each(behaviorEvents, function(behaviour, key) {
|
@@ -3042,7 +3254,6 @@
|
|
3042
3254
|
// for views
|
3043
3255
|
function BehaviorTriggersBuilder(view, behaviors) {
|
3044
3256
|
this._view = view;
|
3045
|
-
this._viewUI = _.result(view, 'ui');
|
3046
3257
|
this._behaviors = behaviors;
|
3047
3258
|
this._triggers = {};
|
3048
3259
|
}
|
@@ -3056,10 +3267,9 @@
|
|
3056
3267
|
|
3057
3268
|
// Internal method to build all trigger handlers for a given behavior
|
3058
3269
|
_buildTriggerHandlersForBehavior: function(behavior, i) {
|
3059
|
-
var ui = _.extend({}, this._viewUI, _.result(behavior, 'ui'));
|
3060
3270
|
var triggersHash = _.clone(_.result(behavior, 'triggers')) || {};
|
3061
3271
|
|
3062
|
-
triggersHash = Marionette.normalizeUIKeys(triggersHash,
|
3272
|
+
triggersHash = Marionette.normalizeUIKeys(triggersHash, getBehaviorsUI(behavior));
|
3063
3273
|
|
3064
3274
|
_.each(triggersHash, _.bind(this._setHandlerForBehavior, this, behavior, i));
|
3065
3275
|
},
|
@@ -3076,6 +3286,10 @@
|
|
3076
3286
|
}
|
3077
3287
|
});
|
3078
3288
|
|
3289
|
+
function getBehaviorsUI(behavior) {
|
3290
|
+
return behavior._uiBindings || behavior.ui;
|
3291
|
+
}
|
3292
|
+
|
3079
3293
|
return Behaviors;
|
3080
3294
|
|
3081
3295
|
})(Marionette, _);
|
@@ -3157,6 +3371,8 @@
|
|
3157
3371
|
this.route(route, methodName, _.bind(method, controller));
|
3158
3372
|
},
|
3159
3373
|
|
3374
|
+
mergeOptions: Marionette.mergeOptions,
|
3375
|
+
|
3160
3376
|
// Proxy `getOption` to enable getting options from this or this.options by name.
|
3161
3377
|
getOption: Marionette.proxyGetOption,
|
3162
3378
|
|
@@ -3464,7 +3680,7 @@
|
|
3464
3680
|
|
3465
3681
|
// get the custom args passed in after the module definition and
|
3466
3682
|
// get rid of the module name and definition function
|
3467
|
-
var customArgs = _.
|
3683
|
+
var customArgs = _.drop(arguments, 3);
|
3468
3684
|
|
3469
3685
|
// Split the module names and get the number of submodules.
|
3470
3686
|
// i.e. an example module name of `Doge.Wow.Amaze` would
|