js_stack 1.5.0 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +4 -4
- data/lib/js_stack/version.rb +1 -1
- data/vendor/assets/javascripts/js_stack/base/marionette/{2.3.0.js → 2.3.1.js} +68 -111
- data/vendor/assets/javascripts/js_stack/base/marionette.js +1 -1
- data/vendor/assets/javascripts/js_stack/plugins/backbone/virtualcollection/0.6.0.js +202 -0
- data/vendor/assets/javascripts/js_stack/plugins/backbone.virtualcollection.js +1 -1
- data/vendor/assets/javascripts/js_stack/plugins/underscore/string/3.0.1.js +1013 -0
- data/vendor/assets/javascripts/js_stack/plugins/underscore.string.js +1 -1
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 109a21b1d4dbcac6bc91e568c3bce32477405e38
|
4
|
+
data.tar.gz: 064f5b1736ef2ac8c4f6a0ae072f211bdeb93749
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d60ef1d9ff3db827d27b746d19f4dcb0e63f20fb0eeeb2cd24338ae519d7e0193e31dfd7059535243aba873ee7d569d458c9004a9846b7dfc700b18d7b345d0f
|
7
|
+
data.tar.gz: faf2447bcb752361755b4ddcc7aeba9bcba99ae776f8ab1dbff1c7b6769f602aef4a19c97a720c54d085f6557cce35a0e82064a3f7f6abc971c367b0c959e547
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -48,7 +48,7 @@ Examples:
|
|
48
48
|
| Library | Versions | Changelog | Homepage |
|
49
49
|
| :-----: | :------: | :-------: | :------: |
|
50
50
|
| backbone | **1.1.2**, 1.0.0 | [changelog](http://backbonejs.org/#changelog) | [homepage](http://backbonejs.org/) |
|
51
|
-
| marionette | **2.3.
|
51
|
+
| marionette | **2.3.1**, 2.2.2, 2.1.0, 2.0.3, 1.8.8, 1.7.4, 1.6.4, 1.5.1, 1.4.1, 1.1.0 | [changelog](https://github.com/marionettejs/backbone.marionette/blob/master/changelog.md) | [homepage](http://marionettejs.com/) |
|
52
52
|
| underscore | **1.7.0**, 1.6.0, 1.5.2 | [changelog](http://underscorejs.org/#changelog) | [homepage](http://underscorejs.org/) |
|
53
53
|
| hamlcoffee | **1.16** | [changelog](https://github.com/netzpirat/haml_coffee_assets/blob/master/CHANGELOG.md) | [homepage](https://github.com/netzpirat/haml_coffee_assets) |
|
54
54
|
| js-routes | **1.0.0** | [changelog](https://github.com/railsware/js-routes/blob/master/CHANGELOG.md) | [homepage](https://github.com/railsware/js-routes) |
|
@@ -65,11 +65,11 @@ Examples:
|
|
65
65
|
| backbone routefilter | **0.2.1** | [changelog](https://github.com/boazsender/backbone.routefilter#release-history) | [homepage](https://github.com/boazsender/backbone.routefilter) |
|
66
66
|
| backbone stickit | **0.8.0**, 0.7.0, 0.6.3 | [changelog](http://nytimes.github.io/backbone.stickit/#change-log) | [homepage](http://nytimes.github.io/backbone.stickit/) |
|
67
67
|
| backbone validation | **0.9.1**, 0.8.1 | [changelog](https://github.com/thedersen/backbone.validation#release-notes) | [homepage](https://github.com/thedersen/backbone.validation) |
|
68
|
-
| backbone virtualcollection | **0.5.3
|
68
|
+
| backbone virtualcollection | **0.6.0**, 0.5.3, 0.4.15 | [changelog](https://github.com/p3drosola/Backbone.VirtualCollection/wiki/Changelog) | [homepage](https://github.com/p3drosola/Backbone.VirtualCollection) |
|
69
69
|
| cocktail | **0.5.8** | None | [homepage](https://github.com/onsi/cocktail) |
|
70
|
-
| momentjs | **2.
|
70
|
+
| momentjs | **2.9.0** | [changelog](https://github.com/moment/moment/blob/develop/CHANGELOG.md) | [homepage](https://github.com/derekprior/momentjs-rails) |
|
71
71
|
| underscore inflections | **0.2.1** | None | [homepage](https://github.com/geetarista/underscore.inflections) |
|
72
|
-
| underscore string | **2.4.0
|
72
|
+
| underscore string | **3.0.1**, 2.4.0, 2.3.2 | [changelog](https://github.com/epeli/underscore.string/blob/master/CHANGELOG.markdown) | [homepage](http://epeli.github.io/underscore.string/) |
|
73
73
|
|
74
74
|
## Contributing
|
75
75
|
|
data/lib/js_stack/version.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
// MarionetteJS (Backbone.Marionette)
|
2
2
|
// ----------------------------------
|
3
|
-
// v2.3.
|
3
|
+
// v2.3.1
|
4
4
|
//
|
5
|
-
// Copyright (c)
|
5
|
+
// Copyright (c)2015 Derick Bailey, Muted Solutions, LLC.
|
6
6
|
// Distributed under MIT license
|
7
7
|
//
|
8
8
|
// http://marionettejs.com
|
@@ -38,7 +38,7 @@
|
|
38
38
|
/* istanbul ignore next */
|
39
39
|
// Backbone.BabySitter
|
40
40
|
// -------------------
|
41
|
-
// v0.1.
|
41
|
+
// v0.1.5
|
42
42
|
//
|
43
43
|
// Copyright (c)2014 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.
|
170
|
+
Backbone.ChildViewContainer.VERSION = "0.1.5";
|
171
171
|
Backbone.ChildViewContainer.noConflict = function() {
|
172
172
|
Backbone.ChildViewContainer = previousChildViewContainer;
|
173
173
|
return this;
|
@@ -493,7 +493,7 @@
|
|
493
493
|
|
494
494
|
var Marionette = Backbone.Marionette = {};
|
495
495
|
|
496
|
-
Marionette.VERSION = '2.3.
|
496
|
+
Marionette.VERSION = '2.3.1';
|
497
497
|
|
498
498
|
Marionette.noConflict = function() {
|
499
499
|
root.Marionette = previousMarionette;
|
@@ -544,6 +544,17 @@
|
|
544
544
|
return Marionette.getOption(this, optionName);
|
545
545
|
};
|
546
546
|
|
547
|
+
// Similar to `_.result`, this is a simple helper
|
548
|
+
// If a function is provided we call it with context
|
549
|
+
// otherwise just return the value. If the value is
|
550
|
+
// undefined return a default value
|
551
|
+
Marionette._getValue = function(value, context, params) {
|
552
|
+
if (_.isFunction(value)) {
|
553
|
+
value = value.apply(context, params);
|
554
|
+
}
|
555
|
+
return value;
|
556
|
+
};
|
557
|
+
|
547
558
|
// Marionette.normalizeMethods
|
548
559
|
// ----------------------
|
549
560
|
|
@@ -802,7 +813,7 @@
|
|
802
813
|
if (!entity || !bindings) { return; }
|
803
814
|
|
804
815
|
// type-check bindings
|
805
|
-
if (!_.
|
816
|
+
if (!_.isObject(bindings)) {
|
806
817
|
throw new Marionette.Error({
|
807
818
|
message: 'Bindings must be an object or function.',
|
808
819
|
url: 'marionette.functions.html#marionettebindentityevents'
|
@@ -810,9 +821,7 @@
|
|
810
821
|
}
|
811
822
|
|
812
823
|
// allow the bindings to be a function
|
813
|
-
|
814
|
-
bindings = bindings.call(target);
|
815
|
-
}
|
824
|
+
bindings = Marionette._getValue(bindings, target);
|
816
825
|
|
817
826
|
// iterate the bindings and bind them
|
818
827
|
_.each(bindings, function(methods, evt) {
|
@@ -1206,24 +1215,18 @@
|
|
1206
1215
|
}
|
1207
1216
|
},
|
1208
1217
|
|
1209
|
-
// Override this method to change how the region finds the
|
1210
|
-
//
|
1218
|
+
// Override this method to change how the region finds the DOM
|
1219
|
+
// element that it manages. Return a jQuery selector object scoped
|
1220
|
+
// to a provided parent el or the document if none exists.
|
1211
1221
|
getEl: function(el) {
|
1212
|
-
return Backbone.$(el);
|
1222
|
+
return Backbone.$(el, Marionette._getValue(this.options.parentEl, this));
|
1213
1223
|
},
|
1214
1224
|
|
1215
1225
|
// Override this method to change how the new view is
|
1216
1226
|
// appended to the `$el` that the region is managing
|
1217
1227
|
attachHtml: function(view) {
|
1218
|
-
|
1219
|
-
|
1220
|
-
// will not let us clear the html of tables and selects.
|
1221
|
-
// We also do not want to use the more declarative `empty` method
|
1222
|
-
// that jquery exposes since `.empty` loops over all of the children DOM
|
1223
|
-
// nodes and unsets the listeners on each node. While this seems like
|
1224
|
-
// a desirable thing, it comes at quite a high perf cost. For that reason
|
1225
|
-
// we are simply clearing the html contents of the node.
|
1226
|
-
this.$el.html('');
|
1228
|
+
this.$el.contents().detach();
|
1229
|
+
|
1227
1230
|
this.el.appendChild(view.el);
|
1228
1231
|
},
|
1229
1232
|
|
@@ -1349,28 +1352,7 @@
|
|
1349
1352
|
options.el = regionConfig.selector;
|
1350
1353
|
}
|
1351
1354
|
|
1352
|
-
|
1353
|
-
|
1354
|
-
// override the `getEl` function if we have a parentEl
|
1355
|
-
// this must be overridden to ensure the selector is found
|
1356
|
-
// on the first use of the region. if we try to assign the
|
1357
|
-
// region's `el` to `parentEl.find(selector)` in the object
|
1358
|
-
// literal to build the region, the element will not be
|
1359
|
-
// guaranteed to be in the DOM already, and will cause problems
|
1360
|
-
if (regionConfig.parentEl) {
|
1361
|
-
region.getEl = function(el) {
|
1362
|
-
if (_.isObject(el)) {
|
1363
|
-
return Backbone.$(el);
|
1364
|
-
}
|
1365
|
-
var parentEl = regionConfig.parentEl;
|
1366
|
-
if (_.isFunction(parentEl)) {
|
1367
|
-
parentEl = parentEl();
|
1368
|
-
}
|
1369
|
-
return parentEl.find(el);
|
1370
|
-
};
|
1371
|
-
}
|
1372
|
-
|
1373
|
-
return region;
|
1355
|
+
return new RegionClass(options);
|
1374
1356
|
},
|
1375
1357
|
|
1376
1358
|
// Build the region directly from a given `RegionClass`
|
@@ -1397,26 +1379,19 @@
|
|
1397
1379
|
// each key becomes the region name, and each value is
|
1398
1380
|
// the region definition.
|
1399
1381
|
addRegions: function(regionDefinitions, defaults) {
|
1400
|
-
|
1401
|
-
regionDefinitions = regionDefinitions.apply(this, arguments);
|
1402
|
-
}
|
1382
|
+
regionDefinitions = Marionette._getValue(regionDefinitions, this, arguments);
|
1403
1383
|
|
1404
|
-
|
1405
|
-
|
1406
|
-
_.each(regionDefinitions, function(definition, name) {
|
1384
|
+
return _.reduce(regionDefinitions, function(regions, definition, name) {
|
1407
1385
|
if (_.isString(definition)) {
|
1408
1386
|
definition = {selector: definition};
|
1409
1387
|
}
|
1410
|
-
|
1411
1388
|
if (definition.selector) {
|
1412
1389
|
definition = _.defaults({}, definition, defaults);
|
1413
1390
|
}
|
1414
1391
|
|
1415
|
-
|
1416
|
-
regions
|
1417
|
-
}, this);
|
1418
|
-
|
1419
|
-
return regions;
|
1392
|
+
regions[name] = this.addRegion(name, definition);
|
1393
|
+
return regions;
|
1394
|
+
}, {}, this);
|
1420
1395
|
},
|
1421
1396
|
|
1422
1397
|
// Add an individual region to the region manager,
|
@@ -1627,12 +1602,7 @@
|
|
1627
1602
|
});
|
1628
1603
|
}
|
1629
1604
|
|
1630
|
-
var templateFunc;
|
1631
|
-
if (typeof template === 'function') {
|
1632
|
-
templateFunc = template;
|
1633
|
-
} else {
|
1634
|
-
templateFunc = Marionette.TemplateCache.get(template);
|
1635
|
-
}
|
1605
|
+
var templateFunc = _.isFunction(template) ? template : Marionette.TemplateCache.get(template);
|
1636
1606
|
|
1637
1607
|
return templateFunc(data);
|
1638
1608
|
}
|
@@ -1645,11 +1615,12 @@
|
|
1645
1615
|
|
1646
1616
|
// The core view class that other Marionette views extend from.
|
1647
1617
|
Marionette.View = Backbone.View.extend({
|
1618
|
+
isDestroyed: false,
|
1648
1619
|
|
1649
1620
|
constructor: function(options) {
|
1650
1621
|
_.bindAll(this, 'render');
|
1651
1622
|
|
1652
|
-
options =
|
1623
|
+
options = Marionette._getValue(options, this);
|
1653
1624
|
|
1654
1625
|
// this exposes view options to the view initializer
|
1655
1626
|
// this is a backfill since backbone removed the assignment
|
@@ -1687,9 +1658,7 @@
|
|
1687
1658
|
mixinTemplateHelpers: function(target) {
|
1688
1659
|
target = target || {};
|
1689
1660
|
var templateHelpers = this.getOption('templateHelpers');
|
1690
|
-
|
1691
|
-
templateHelpers = templateHelpers.call(this);
|
1692
|
-
}
|
1661
|
+
templateHelpers = Marionette._getValue(templateHelpers, this);
|
1693
1662
|
return _.extend(target, templateHelpers);
|
1694
1663
|
},
|
1695
1664
|
|
@@ -1741,8 +1710,7 @@
|
|
1741
1710
|
|
1742
1711
|
// internal method to delegate DOM events and triggers
|
1743
1712
|
_delegateDOMEvents: function(eventsArg) {
|
1744
|
-
var events = eventsArg || this.events;
|
1745
|
-
if (_.isFunction(events)) { events = events.call(this); }
|
1713
|
+
var events = Marionette._getValue(eventsArg || this.events, this);
|
1746
1714
|
|
1747
1715
|
// normalize ui keys
|
1748
1716
|
events = this.normalizeUIKeys(events);
|
@@ -1845,8 +1813,7 @@
|
|
1845
1813
|
this.ui = {};
|
1846
1814
|
|
1847
1815
|
// bind each of the selectors
|
1848
|
-
_.each(
|
1849
|
-
var selector = bindings[key];
|
1816
|
+
_.each(bindings, function(selector, key) {
|
1850
1817
|
this.ui[key] = this.$(selector);
|
1851
1818
|
}, this);
|
1852
1819
|
},
|
@@ -2342,9 +2309,7 @@
|
|
2342
2309
|
// in order to keep the children in sync with the collection.
|
2343
2310
|
addChild: function(child, ChildView, index) {
|
2344
2311
|
var childViewOptions = this.getOption('childViewOptions');
|
2345
|
-
|
2346
|
-
childViewOptions = childViewOptions.call(this, child, index);
|
2347
|
-
}
|
2312
|
+
childViewOptions = Marionette._getValue(childViewOptions, this, [child, index]);
|
2348
2313
|
|
2349
2314
|
var view = this.buildChildView(child, ChildView, childViewOptions);
|
2350
2315
|
|
@@ -2368,22 +2333,14 @@
|
|
2368
2333
|
if (increment) {
|
2369
2334
|
// assign the index to the view
|
2370
2335
|
view._index = index;
|
2371
|
-
|
2372
|
-
// increment the index of views after this one
|
2373
|
-
this.children.each(function (laterView) {
|
2374
|
-
if (laterView._index >= view._index) {
|
2375
|
-
laterView._index++;
|
2376
|
-
}
|
2377
|
-
});
|
2378
|
-
}
|
2379
|
-
else {
|
2380
|
-
// decrement the index of views after this one
|
2381
|
-
this.children.each(function (laterView) {
|
2382
|
-
if (laterView._index >= view._index) {
|
2383
|
-
laterView._index--;
|
2384
|
-
}
|
2385
|
-
});
|
2386
2336
|
}
|
2337
|
+
|
2338
|
+
// update the indexes of views after this one
|
2339
|
+
this.children.each(function (laterView) {
|
2340
|
+
if (laterView._index >= view._index) {
|
2341
|
+
laterView._index += increment ? 1 : -1;
|
2342
|
+
}
|
2343
|
+
});
|
2387
2344
|
},
|
2388
2345
|
|
2389
2346
|
|
@@ -2707,7 +2664,7 @@
|
|
2707
2664
|
var childViewContainer = Marionette.getOption(containerView, 'childViewContainer');
|
2708
2665
|
if (childViewContainer) {
|
2709
2666
|
|
2710
|
-
var selector =
|
2667
|
+
var selector = Marionette._getValue(childViewContainer, containerView);
|
2711
2668
|
|
2712
2669
|
if (selector.charAt(0) === '@' && containerView.ui) {
|
2713
2670
|
container = containerView.ui[selector.substr(4)];
|
@@ -2824,7 +2781,7 @@
|
|
2824
2781
|
_buildRegions: function(regions) {
|
2825
2782
|
var defaults = {
|
2826
2783
|
regionClass: this.getOption('regionClass'),
|
2827
|
-
parentEl: _.partial(_.result, this, '
|
2784
|
+
parentEl: _.partial(_.result, this, 'el')
|
2828
2785
|
};
|
2829
2786
|
|
2830
2787
|
return this.regionManager.addRegions(regions, defaults);
|
@@ -2836,19 +2793,13 @@
|
|
2836
2793
|
var regions;
|
2837
2794
|
this._initRegionManager();
|
2838
2795
|
|
2839
|
-
|
2840
|
-
regions = this.regions(options);
|
2841
|
-
} else {
|
2842
|
-
regions = this.regions || {};
|
2843
|
-
}
|
2796
|
+
regions = Marionette._getValue(this.regions, this, [options]) || {};
|
2844
2797
|
|
2845
2798
|
// Enable users to define `regions` as instance options.
|
2846
2799
|
var regionOptions = this.getOption.call(options, 'regions');
|
2847
2800
|
|
2848
2801
|
// enable region options to be a function
|
2849
|
-
|
2850
|
-
regionOptions = regionOptions.call(this, options);
|
2851
|
-
}
|
2802
|
+
regionOptions = Marionette._getValue(regionOptions, this, [options]);
|
2852
2803
|
|
2853
2804
|
_.extend(regions, regionOptions);
|
2854
2805
|
|
@@ -2957,6 +2908,8 @@
|
|
2957
2908
|
// method for things to work properly.
|
2958
2909
|
|
2959
2910
|
Marionette.Behaviors = (function(Marionette, _) {
|
2911
|
+
// Borrow event splitter from Backbone
|
2912
|
+
var delegateEventSplitter = /^(\S+)\s*(.*)$/;
|
2960
2913
|
|
2961
2914
|
function Behaviors(view, behaviors) {
|
2962
2915
|
|
@@ -2983,12 +2936,12 @@
|
|
2983
2936
|
|
2984
2937
|
behaviorEvents: function(behaviorEvents, behaviors) {
|
2985
2938
|
var _behaviorsEvents = {};
|
2986
|
-
var viewUI = _.result(this, 'ui');
|
2939
|
+
var viewUI = this._uiBindings || _.result(this, 'ui');
|
2987
2940
|
|
2988
2941
|
_.each(behaviors, function(b, i) {
|
2989
2942
|
var _events = {};
|
2990
2943
|
var behaviorEvents = _.clone(_.result(b, 'events')) || {};
|
2991
|
-
var behaviorUI = _.result(b, 'ui');
|
2944
|
+
var behaviorUI = b._uiBindings || _.result(b, 'ui');
|
2992
2945
|
|
2993
2946
|
// Construct an internal UI hash first using
|
2994
2947
|
// the views UI hash and then the behaviors UI hash.
|
@@ -3001,21 +2954,25 @@
|
|
3001
2954
|
// a user to use the @ui. syntax.
|
3002
2955
|
behaviorEvents = Marionette.normalizeUIKeys(behaviorEvents, ui);
|
3003
2956
|
|
3004
|
-
|
3005
|
-
|
3006
|
-
|
3007
|
-
|
2957
|
+
var j = 0;
|
2958
|
+
_.each(behaviorEvents, function(behaviour, key) {
|
2959
|
+
var match = key.match(delegateEventSplitter);
|
2960
|
+
|
2961
|
+
// Set event name to be namespaced using the view cid,
|
2962
|
+
// the behavior index, and the behavior event index
|
2963
|
+
// to generate a non colliding event namespace
|
2964
|
+
// http://api.jquery.com/event.namespace/
|
2965
|
+
var eventName = match[1] + '.' + [this.cid, i, j++, ' '].join(''),
|
2966
|
+
selector = match[2];
|
3008
2967
|
|
3009
|
-
|
3010
|
-
var
|
3011
|
-
var eventKey = key + whitespace;
|
3012
|
-
var handler = _.isFunction(behaviorEvents[key]) ? behaviorEvents[key] : b[behaviorEvents[key]];
|
2968
|
+
var eventKey = eventName + selector;
|
2969
|
+
var handler = _.isFunction(behaviour) ? behaviour : b[behaviour];
|
3013
2970
|
|
3014
2971
|
_events[eventKey] = _.bind(handler, b);
|
3015
|
-
});
|
2972
|
+
}, this);
|
3016
2973
|
|
3017
2974
|
_behaviorsEvents = _.extend(_behaviorsEvents, _events);
|
3018
|
-
});
|
2975
|
+
}, this);
|
3019
2976
|
|
3020
2977
|
return _behaviorsEvents;
|
3021
2978
|
}
|
@@ -3050,7 +3007,7 @@
|
|
3050
3007
|
}
|
3051
3008
|
|
3052
3009
|
// Get behavior class can be either a flat object or a method
|
3053
|
-
return
|
3010
|
+
return Marionette._getValue(Behaviors.behaviorsLookup, this, [options, key])[key];
|
3054
3011
|
},
|
3055
3012
|
|
3056
3013
|
// Iterate over the behaviors object, for each behavior
|
@@ -3101,7 +3058,7 @@
|
|
3101
3058
|
|
3102
3059
|
triggersHash = Marionette.normalizeUIKeys(triggersHash, ui);
|
3103
3060
|
|
3104
|
-
_.each(triggersHash, _.
|
3061
|
+
_.each(triggersHash, _.bind(this._setHandlerForBehavior, this, behavior, i));
|
3105
3062
|
},
|
3106
3063
|
|
3107
3064
|
// Internal method to create and assign the trigger handler for a given
|
@@ -1 +1 @@
|
|
1
|
-
//= require js_stack/base/marionette/2.3.
|
1
|
+
//= require js_stack/base/marionette/2.3.1
|
@@ -0,0 +1,202 @@
|
|
1
|
+
// Available under the MIT License (MIT)
|
2
|
+
|
3
|
+
var VirtualCollection,
|
4
|
+
Backbone = require('backbone'),
|
5
|
+
_ = require('underscore');
|
6
|
+
|
7
|
+
VirtualCollection = Backbone.Collection.extend({
|
8
|
+
|
9
|
+
constructor: function (collection, options) {
|
10
|
+
options = options || {};
|
11
|
+
this.collection = collection;
|
12
|
+
|
13
|
+
if (options.comparator !== undefined) this.comparator = options.comparator;
|
14
|
+
if (options.close_with) this.bindLifecycle(options.close_with, 'close'); // Marionette 1.*
|
15
|
+
if (options.destroy_with) this.bindLifecycle(options.destroy_with, 'destroy'); // Marionette 2.*
|
16
|
+
if (!this.model) this.model = collection.model;
|
17
|
+
|
18
|
+
this.accepts = VirtualCollection.buildFilter(options.filter);
|
19
|
+
this._rebuildIndex();
|
20
|
+
this.listenTo(this.collection, 'add', this._onAdd);
|
21
|
+
this.listenTo(this.collection, 'remove', this._onRemove);
|
22
|
+
this.listenTo(this.collection, 'change', this._onChange);
|
23
|
+
this.listenTo(this.collection, 'reset', this._onReset);
|
24
|
+
this.listenTo(this.collection, 'sort', this._onSort);
|
25
|
+
|
26
|
+
this.initialize.apply(this, arguments);
|
27
|
+
},
|
28
|
+
|
29
|
+
// Marionette 1.*
|
30
|
+
bindLifecycle: function (view, method_name) {
|
31
|
+
view.on(method_name, _.bind(this.stopListening, this));
|
32
|
+
},
|
33
|
+
|
34
|
+
updateFilter: function (filter) {
|
35
|
+
this.accepts = VirtualCollection.buildFilter(filter);
|
36
|
+
this._rebuildIndex();
|
37
|
+
this.trigger('filter', this, filter);
|
38
|
+
this.trigger('reset', this, filter);
|
39
|
+
return this;
|
40
|
+
},
|
41
|
+
|
42
|
+
_rebuildIndex: function () {
|
43
|
+
for(idx in this.models) {
|
44
|
+
this.models[idx].off('all', this._onModelEvent, this);
|
45
|
+
}
|
46
|
+
this._reset();
|
47
|
+
this.collection.each(function (model, i) {
|
48
|
+
if (this.accepts(model, i)) {
|
49
|
+
model.on('all', this._onModelEvent, this);
|
50
|
+
this.models.push(model);
|
51
|
+
this._byId[model.cid] = model;
|
52
|
+
if (model.id) this._byId[model.id] = model;
|
53
|
+
}
|
54
|
+
}, this);
|
55
|
+
this.length = this.models.length;
|
56
|
+
|
57
|
+
if (this.comparator) this.sort({silent: true});
|
58
|
+
},
|
59
|
+
|
60
|
+
orderViaParent: function (options) {
|
61
|
+
this.models = this.collection.filter(function (model) {
|
62
|
+
return (this._byId[model.cid] !== undefined);
|
63
|
+
}, this);
|
64
|
+
if (!options.silent) this.trigger('sort', this, options);
|
65
|
+
},
|
66
|
+
|
67
|
+
_onSort: function (collection, options) {
|
68
|
+
if (this.comparator !== undefined) return;
|
69
|
+
this.orderViaParent(options);
|
70
|
+
},
|
71
|
+
|
72
|
+
_onAdd: function (model, collection, options) {
|
73
|
+
var already_here = this.get(model);
|
74
|
+
if (!already_here && this.accepts(model, options.index)) {
|
75
|
+
this._indexAdd(model);
|
76
|
+
model.on('all', this._onModelEvent, this);
|
77
|
+
this.trigger('add', model, this, options);
|
78
|
+
}
|
79
|
+
},
|
80
|
+
|
81
|
+
_onRemove: function (model, collection, options) {
|
82
|
+
if (!this.get(model)) return;
|
83
|
+
|
84
|
+
var i = this._indexRemove(model)
|
85
|
+
, options_clone = _.clone(options);
|
86
|
+
options_clone.index = i;
|
87
|
+
model.off('all', this._onModelEvent, this);
|
88
|
+
this.trigger('remove', model, this, options_clone);
|
89
|
+
},
|
90
|
+
|
91
|
+
_onChange: function (model, options) {
|
92
|
+
if (!model || !options) return; // ignore malformed arguments coming from custom events
|
93
|
+
var already_here = this.get(model);
|
94
|
+
|
95
|
+
if (this.accepts(model, options.index)) {
|
96
|
+
if (already_here) {
|
97
|
+
this.trigger('change', model, this, options);
|
98
|
+
} else {
|
99
|
+
this._indexAdd(model);
|
100
|
+
this.trigger('add', model, this, options);
|
101
|
+
}
|
102
|
+
} else {
|
103
|
+
if (already_here) {
|
104
|
+
var i = this._indexRemove(model)
|
105
|
+
, options_clone = _.clone(options);
|
106
|
+
options_clone.index = i;
|
107
|
+
this.trigger('remove', model, this, options_clone);
|
108
|
+
}
|
109
|
+
}
|
110
|
+
},
|
111
|
+
|
112
|
+
_onReset: function (collection, options) {
|
113
|
+
this._rebuildIndex();
|
114
|
+
this.trigger('reset', this, options);
|
115
|
+
},
|
116
|
+
|
117
|
+
sortedIndex: function (model, value, context) {
|
118
|
+
var iterator = _.isFunction(value) ? value : function(target) {
|
119
|
+
return target.get(value);
|
120
|
+
};
|
121
|
+
|
122
|
+
if (iterator.length == 1) {
|
123
|
+
return _.sortedIndex(this.models, model, iterator, context);
|
124
|
+
} else {
|
125
|
+
return sortedIndexTwo(this.models, model, iterator, context);
|
126
|
+
}
|
127
|
+
},
|
128
|
+
|
129
|
+
_indexAdd: function (model) {
|
130
|
+
if (this.get(model)) return;
|
131
|
+
var i;
|
132
|
+
// uses a binsearch to find the right index
|
133
|
+
if (this.comparator) {
|
134
|
+
i = this.sortedIndex(model, this.comparator, this);
|
135
|
+
} else if (this.comparator === undefined) {
|
136
|
+
i = this.sortedIndex(model, function (target) {
|
137
|
+
//TODO: indexOf traverses the array every time the iterator is called
|
138
|
+
return this.collection.indexOf(target);
|
139
|
+
}, this);
|
140
|
+
} else {
|
141
|
+
i = this.length;
|
142
|
+
}
|
143
|
+
this.models.splice(i, 0, model);
|
144
|
+
this._byId[model.cid] = model;
|
145
|
+
if (model.id) this._byId[model.id] = model;
|
146
|
+
this.length += 1;
|
147
|
+
},
|
148
|
+
|
149
|
+
_indexRemove: function (model) {
|
150
|
+
model.off('all', this._onModelEvent, this);
|
151
|
+
var i = this.indexOf(model);
|
152
|
+
if (i === -1) return i;
|
153
|
+
this.models.splice(i, 1);
|
154
|
+
delete this._byId[model.cid];
|
155
|
+
if (model.id) delete this._byId[model.id];
|
156
|
+
this.length -= 1;
|
157
|
+
return i;
|
158
|
+
}
|
159
|
+
|
160
|
+
}, { // static props
|
161
|
+
|
162
|
+
buildFilter: function (options) {
|
163
|
+
if (!options) {
|
164
|
+
return function () {
|
165
|
+
return true;
|
166
|
+
};
|
167
|
+
} else if (_.isFunction(options)) {
|
168
|
+
return options;
|
169
|
+
} else if (options.constructor === Object) {
|
170
|
+
return function (model) {
|
171
|
+
return !Boolean(_(Object.keys(options)).detect(function (key) {
|
172
|
+
return model.get(key) !== options[key];
|
173
|
+
}));
|
174
|
+
};
|
175
|
+
}
|
176
|
+
}
|
177
|
+
});
|
178
|
+
|
179
|
+
// methods that alter data should proxy to the parent collection
|
180
|
+
_.each(['add', 'remove', 'set', 'reset', 'push', 'pop', 'unshift', 'shift', 'slice', 'sync', 'fetch'], function (method_name) {
|
181
|
+
VirtualCollection.prototype[method_name] = function () {
|
182
|
+
return this.collection[method_name].apply(this.collection, _.toArray(arguments));
|
183
|
+
};
|
184
|
+
});
|
185
|
+
|
186
|
+
/**
|
187
|
+
|
188
|
+
Equivalent to _.sortedIndex, but for comparators with two arguments
|
189
|
+
|
190
|
+
**/
|
191
|
+
function sortedIndexTwo (array, obj, iterator, context) {
|
192
|
+
var low = 0, high = array.length;
|
193
|
+
while (low < high) {
|
194
|
+
var mid = (low + high) >>> 1;
|
195
|
+
iterator.call(context, obj, array[mid]) > 0 ? low = mid + 1 : high = mid;
|
196
|
+
}
|
197
|
+
return low;
|
198
|
+
}
|
199
|
+
|
200
|
+
_.extend(VirtualCollection.prototype, Backbone.Events);
|
201
|
+
|
202
|
+
module.exports = VirtualCollection;
|
@@ -1 +1 @@
|
|
1
|
-
//= require js_stack/plugins/backbone/virtualcollection/0.
|
1
|
+
//= require js_stack/plugins/backbone/virtualcollection/0.6.0
|