marionette.modal 1.0.0.6 → 1.0.0.7
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 +4 -4
- data/Gruntfile.coffee +1 -1
- data/examples/vendor/backbone.js +189 -169
- data/examples/vendor/marionette.js +287 -161
- data/examples/vendor/underscore.js +210 -123
- data/lib/marionette.modal/version.rb +1 -1
- data/test/spec/backbone.marionette.modals.spec.js +46 -13
- data/test/src/backbone.marionette.modals.spec.coffee +13 -4
- metadata +1 -1
data/examples/vendor/backbone.js
CHANGED
@@ -1,19 +1,37 @@
|
|
1
|
-
// Backbone.js 1.
|
1
|
+
// Backbone.js 1.1.0
|
2
2
|
|
3
|
-
// (c) 2010-
|
3
|
+
// (c) 2010-2011 Jeremy Ashkenas, DocumentCloud Inc.
|
4
|
+
// (c) 2011-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
|
4
5
|
// Backbone may be freely distributed under the MIT license.
|
5
6
|
// For all details and documentation:
|
6
7
|
// http://backbonejs.org
|
7
8
|
|
8
|
-
(function(){
|
9
|
+
(function(root, factory) {
|
10
|
+
|
11
|
+
// Set up Backbone appropriately for the environment. Start with AMD.
|
12
|
+
if (typeof define === 'function' && define.amd) {
|
13
|
+
define(['underscore', 'jquery', 'exports'], function(_, $, exports) {
|
14
|
+
// Export global even in AMD case in case this script is loaded with
|
15
|
+
// others that may still expect a global Backbone.
|
16
|
+
root.Backbone = factory(root, exports, _, $);
|
17
|
+
});
|
18
|
+
|
19
|
+
// Next for Node.js or CommonJS. jQuery may not be needed as a module.
|
20
|
+
} else if (typeof exports !== 'undefined') {
|
21
|
+
var _ = require('underscore'), $;
|
22
|
+
try { $ = require('jquery'); } catch(e) {};
|
23
|
+
factory(root, exports, _, $);
|
24
|
+
|
25
|
+
// Finally, as a browser global.
|
26
|
+
} else {
|
27
|
+
root.Backbone = factory(root, {}, root._, (root.jQuery || root.Zepto || root.ender || root.$));
|
28
|
+
}
|
29
|
+
|
30
|
+
}(this, function(root, Backbone, _, $) {
|
9
31
|
|
10
32
|
// Initial Setup
|
11
33
|
// -------------
|
12
34
|
|
13
|
-
// Save a reference to the global object (`window` in the browser, `exports`
|
14
|
-
// on the server).
|
15
|
-
var root = this;
|
16
|
-
|
17
35
|
// Save the previous value of the `Backbone` variable, so that it can be
|
18
36
|
// restored later on, if `noConflict` is used.
|
19
37
|
var previousBackbone = root.Backbone;
|
@@ -24,25 +42,12 @@
|
|
24
42
|
var slice = array.slice;
|
25
43
|
var splice = array.splice;
|
26
44
|
|
27
|
-
// The top-level namespace. All public Backbone classes and modules will
|
28
|
-
// be attached to this. Exported for both the browser and the server.
|
29
|
-
var Backbone;
|
30
|
-
if (typeof exports !== 'undefined') {
|
31
|
-
Backbone = exports;
|
32
|
-
} else {
|
33
|
-
Backbone = root.Backbone = {};
|
34
|
-
}
|
35
|
-
|
36
45
|
// Current version of the library. Keep in sync with `package.json`.
|
37
|
-
Backbone.VERSION = '1.
|
38
|
-
|
39
|
-
// Require Underscore, if we're on the server, and it's not already present.
|
40
|
-
var _ = root._;
|
41
|
-
if (!_ && (typeof require !== 'undefined')) _ = require('underscore');
|
46
|
+
Backbone.VERSION = '1.1.0';
|
42
47
|
|
43
48
|
// For Backbone's purposes, jQuery, Zepto, Ender, or My Library (kidding) owns
|
44
49
|
// the `$` variable.
|
45
|
-
Backbone.$ =
|
50
|
+
Backbone.$ = $;
|
46
51
|
|
47
52
|
// Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable
|
48
53
|
// to its previous owner. Returns a reference to this Backbone object.
|
@@ -52,7 +57,7 @@
|
|
52
57
|
};
|
53
58
|
|
54
59
|
// Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option
|
55
|
-
// will fake `"PUT"` and `"DELETE"` requests via the `_method` parameter and
|
60
|
+
// will fake `"PATCH"`, `"PUT"` and `"DELETE"` requests via the `_method` parameter and
|
56
61
|
// set a `X-Http-Method-Override` header.
|
57
62
|
Backbone.emulateHTTP = false;
|
58
63
|
|
@@ -108,10 +113,9 @@
|
|
108
113
|
var retain, ev, events, names, i, l, j, k;
|
109
114
|
if (!this._events || !eventsApi(this, 'off', name, [callback, context])) return this;
|
110
115
|
if (!name && !callback && !context) {
|
111
|
-
this._events =
|
116
|
+
this._events = void 0;
|
112
117
|
return this;
|
113
118
|
}
|
114
|
-
|
115
119
|
names = name ? [name] : _.keys(this._events);
|
116
120
|
for (i = 0, l = names.length; i < l; i++) {
|
117
121
|
name = names[i];
|
@@ -151,14 +155,15 @@
|
|
151
155
|
// Tell this object to stop listening to either specific events ... or
|
152
156
|
// to every object it's currently listening to.
|
153
157
|
stopListening: function(obj, name, callback) {
|
154
|
-
var
|
155
|
-
if (!
|
156
|
-
var
|
157
|
-
if (typeof name === 'object') callback = this;
|
158
|
-
if (obj) (
|
159
|
-
for (var id in
|
160
|
-
|
161
|
-
|
158
|
+
var listeningTo = this._listeningTo;
|
159
|
+
if (!listeningTo) return this;
|
160
|
+
var remove = !name && !callback;
|
161
|
+
if (!callback && typeof name === 'object') callback = this;
|
162
|
+
if (obj) (listeningTo = {})[obj._listenId] = obj;
|
163
|
+
for (var id in listeningTo) {
|
164
|
+
obj = listeningTo[id];
|
165
|
+
obj.off(name, callback, this);
|
166
|
+
if (remove || _.isEmpty(obj._events)) delete this._listeningTo[id];
|
162
167
|
}
|
163
168
|
return this;
|
164
169
|
}
|
@@ -215,10 +220,10 @@
|
|
215
220
|
// listening to.
|
216
221
|
_.each(listenMethods, function(implementation, method) {
|
217
222
|
Events[method] = function(obj, name, callback) {
|
218
|
-
var
|
219
|
-
var id = obj.
|
220
|
-
|
221
|
-
if (typeof name === 'object') callback = this;
|
223
|
+
var listeningTo = this._listeningTo || (this._listeningTo = {});
|
224
|
+
var id = obj._listenId || (obj._listenId = _.uniqueId('l'));
|
225
|
+
listeningTo[id] = obj;
|
226
|
+
if (!callback && typeof name === 'object') callback = this;
|
222
227
|
obj[implementation](name, callback, this);
|
223
228
|
return this;
|
224
229
|
};
|
@@ -243,24 +248,18 @@
|
|
243
248
|
// Create a new model with the specified attributes. A client id (`cid`)
|
244
249
|
// is automatically generated and assigned for you.
|
245
250
|
var Model = Backbone.Model = function(attributes, options) {
|
246
|
-
var defaults;
|
247
251
|
var attrs = attributes || {};
|
248
252
|
options || (options = {});
|
249
253
|
this.cid = _.uniqueId('c');
|
250
254
|
this.attributes = {};
|
251
|
-
|
255
|
+
if (options.collection) this.collection = options.collection;
|
252
256
|
if (options.parse) attrs = this.parse(attrs, options) || {};
|
253
|
-
|
254
|
-
attrs = _.defaults({}, attrs, defaults);
|
255
|
-
}
|
257
|
+
attrs = _.defaults({}, attrs, _.result(this, 'defaults'));
|
256
258
|
this.set(attrs, options);
|
257
259
|
this.changed = {};
|
258
260
|
this.initialize.apply(this, arguments);
|
259
261
|
};
|
260
262
|
|
261
|
-
// A list of options to be attached directly to the model, if provided.
|
262
|
-
var modelOptions = ['url', 'urlRoot', 'collection'];
|
263
|
-
|
264
263
|
// Attach all inheritable methods to the Model prototype.
|
265
264
|
_.extend(Model.prototype, Events, {
|
266
265
|
|
@@ -355,7 +354,7 @@
|
|
355
354
|
|
356
355
|
// Trigger all relevant attribute changes.
|
357
356
|
if (!silent) {
|
358
|
-
if (changes.length) this._pending =
|
357
|
+
if (changes.length) this._pending = options;
|
359
358
|
for (var i = 0, l = changes.length; i < l; i++) {
|
360
359
|
this.trigger('change:' + changes[i], this, current[changes[i]], options);
|
361
360
|
}
|
@@ -366,6 +365,7 @@
|
|
366
365
|
if (changing) return this;
|
367
366
|
if (!silent) {
|
368
367
|
while (this._pending) {
|
368
|
+
options = this._pending;
|
369
369
|
this._pending = false;
|
370
370
|
this.trigger('change', this, options);
|
371
371
|
}
|
@@ -456,13 +456,16 @@
|
|
456
456
|
(attrs = {})[key] = val;
|
457
457
|
}
|
458
458
|
|
459
|
-
// If we're not waiting and attributes exist, save acts as `set(attr).save(null, opts)`.
|
460
|
-
if (attrs && (!options || !options.wait) && !this.set(attrs, options)) return false;
|
461
|
-
|
462
459
|
options = _.extend({validate: true}, options);
|
463
460
|
|
464
|
-
//
|
465
|
-
|
461
|
+
// If we're not waiting and attributes exist, save acts as
|
462
|
+
// `set(attr).save(null, opts)` with validation. Otherwise, check if
|
463
|
+
// the model will be valid when the attributes, if any, are set.
|
464
|
+
if (attrs && !options.wait) {
|
465
|
+
if (!this.set(attrs, options)) return false;
|
466
|
+
} else {
|
467
|
+
if (!this._validate(attrs, options)) return false;
|
468
|
+
}
|
466
469
|
|
467
470
|
// Set temporary attributes if `{wait: true}`.
|
468
471
|
if (attrs && options.wait) {
|
@@ -548,7 +551,7 @@
|
|
548
551
|
|
549
552
|
// A model is new if it has never been saved to the server, and lacks an id.
|
550
553
|
isNew: function() {
|
551
|
-
return this.
|
554
|
+
return !this.has(this.idAttribute);
|
552
555
|
},
|
553
556
|
|
554
557
|
// Check if the model is currently in a valid state.
|
@@ -563,7 +566,7 @@
|
|
563
566
|
attrs = _.extend({}, this.attributes, attrs);
|
564
567
|
var error = this.validationError = this.validate(attrs, options) || null;
|
565
568
|
if (!error) return true;
|
566
|
-
this.trigger('invalid', this, error, _.extend(options
|
569
|
+
this.trigger('invalid', this, error, _.extend(options, {validationError: error}));
|
567
570
|
return false;
|
568
571
|
}
|
569
572
|
|
@@ -596,7 +599,6 @@
|
|
596
599
|
// its models in sort order, as they're added and removed.
|
597
600
|
var Collection = Backbone.Collection = function(models, options) {
|
598
601
|
options || (options = {});
|
599
|
-
if (options.url) this.url = options.url;
|
600
602
|
if (options.model) this.model = options.model;
|
601
603
|
if (options.comparator !== void 0) this.comparator = options.comparator;
|
602
604
|
this._reset();
|
@@ -606,7 +608,7 @@
|
|
606
608
|
|
607
609
|
// Default options for `Collection#set`.
|
608
610
|
var setOptions = {add: true, remove: true, merge: true};
|
609
|
-
var addOptions = {add: true,
|
611
|
+
var addOptions = {add: true, remove: false};
|
610
612
|
|
611
613
|
// Define the Collection's inheritable methods.
|
612
614
|
_.extend(Collection.prototype, Events, {
|
@@ -632,19 +634,18 @@
|
|
632
634
|
|
633
635
|
// Add a model, or list of models to the set.
|
634
636
|
add: function(models, options) {
|
635
|
-
return this.set(models, _.
|
637
|
+
return this.set(models, _.extend({merge: false}, options, addOptions));
|
636
638
|
},
|
637
639
|
|
638
640
|
// Remove a model, or a list of models from the set.
|
639
641
|
remove: function(models, options) {
|
640
|
-
|
642
|
+
var singular = !_.isArray(models);
|
643
|
+
models = singular ? [models] : _.clone(models);
|
641
644
|
options || (options = {});
|
642
645
|
var i, l, index, model;
|
643
646
|
for (i = 0, l = models.length; i < l; i++) {
|
644
|
-
model = this.get(models[i]);
|
647
|
+
model = models[i] = this.get(models[i]);
|
645
648
|
if (!model) continue;
|
646
|
-
delete this._byId[model.id];
|
647
|
-
delete this._byId[model.cid];
|
648
649
|
index = this.indexOf(model);
|
649
650
|
this.models.splice(index, 1);
|
650
651
|
this.length--;
|
@@ -652,9 +653,9 @@
|
|
652
653
|
options.index = index;
|
653
654
|
model.trigger('remove', model, this, options);
|
654
655
|
}
|
655
|
-
this._removeReference(model);
|
656
|
+
this._removeReference(model, options);
|
656
657
|
}
|
657
|
-
return
|
658
|
+
return singular ? models[0] : models;
|
658
659
|
},
|
659
660
|
|
660
661
|
// Update a collection by `set`-ing a new list of models, adding new ones,
|
@@ -662,43 +663,53 @@
|
|
662
663
|
// already exist in the collection, as necessary. Similar to **Model#set**,
|
663
664
|
// the core operation for updating the data contained by the collection.
|
664
665
|
set: function(models, options) {
|
665
|
-
options = _.defaults(
|
666
|
+
options = _.defaults({}, options, setOptions);
|
666
667
|
if (options.parse) models = this.parse(models, options);
|
667
|
-
|
668
|
-
|
668
|
+
var singular = !_.isArray(models);
|
669
|
+
models = singular ? (models ? [models] : []) : _.clone(models);
|
670
|
+
var i, l, id, model, attrs, existing, sort;
|
669
671
|
var at = options.at;
|
672
|
+
var targetModel = this.model;
|
670
673
|
var sortable = this.comparator && (at == null) && options.sort !== false;
|
671
674
|
var sortAttr = _.isString(this.comparator) ? this.comparator : null;
|
672
675
|
var toAdd = [], toRemove = [], modelMap = {};
|
676
|
+
var add = options.add, merge = options.merge, remove = options.remove;
|
677
|
+
var order = !sortable && add && remove ? [] : false;
|
673
678
|
|
674
679
|
// Turn bare objects into model references, and prevent invalid models
|
675
680
|
// from being added.
|
676
681
|
for (i = 0, l = models.length; i < l; i++) {
|
677
|
-
|
682
|
+
attrs = models[i] || {};
|
683
|
+
if (attrs instanceof Model) {
|
684
|
+
id = model = attrs;
|
685
|
+
} else {
|
686
|
+
id = attrs[targetModel.prototype.idAttribute || 'id'];
|
687
|
+
}
|
678
688
|
|
679
689
|
// If a duplicate is found, prevent it from being added and
|
680
690
|
// optionally merge it into the existing model.
|
681
|
-
if (existing = this.get(
|
682
|
-
if (
|
683
|
-
if (
|
684
|
-
|
691
|
+
if (existing = this.get(id)) {
|
692
|
+
if (remove) modelMap[existing.cid] = true;
|
693
|
+
if (merge) {
|
694
|
+
attrs = attrs === model ? model.attributes : attrs;
|
695
|
+
if (options.parse) attrs = existing.parse(attrs, options);
|
696
|
+
existing.set(attrs, options);
|
685
697
|
if (sortable && !sort && existing.hasChanged(sortAttr)) sort = true;
|
686
698
|
}
|
699
|
+
models[i] = existing;
|
687
700
|
|
688
|
-
//
|
689
|
-
} else if (
|
701
|
+
// If this is a new, valid model, push it to the `toAdd` list.
|
702
|
+
} else if (add) {
|
703
|
+
model = models[i] = this._prepareModel(attrs, options);
|
704
|
+
if (!model) continue;
|
690
705
|
toAdd.push(model);
|
691
|
-
|
692
|
-
// Listen to added models' events, and index models for lookup by
|
693
|
-
// `id` and by `cid`.
|
694
|
-
model.on('all', this._onModelEvent, this);
|
695
|
-
this._byId[model.cid] = model;
|
696
|
-
if (model.id != null) this._byId[model.id] = model;
|
706
|
+
this._addReference(model, options);
|
697
707
|
}
|
708
|
+
if (order) order.push(existing || model);
|
698
709
|
}
|
699
710
|
|
700
711
|
// Remove nonexistent models if appropriate.
|
701
|
-
if (
|
712
|
+
if (remove) {
|
702
713
|
for (i = 0, l = this.length; i < l; ++i) {
|
703
714
|
if (!modelMap[(model = this.models[i]).cid]) toRemove.push(model);
|
704
715
|
}
|
@@ -706,29 +717,35 @@
|
|
706
717
|
}
|
707
718
|
|
708
719
|
// See if sorting is needed, update `length` and splice in new models.
|
709
|
-
if (toAdd.length) {
|
720
|
+
if (toAdd.length || (order && order.length)) {
|
710
721
|
if (sortable) sort = true;
|
711
722
|
this.length += toAdd.length;
|
712
723
|
if (at != null) {
|
713
|
-
|
724
|
+
for (i = 0, l = toAdd.length; i < l; i++) {
|
725
|
+
this.models.splice(at + i, 0, toAdd[i]);
|
726
|
+
}
|
714
727
|
} else {
|
715
|
-
|
728
|
+
if (order) this.models.length = 0;
|
729
|
+
var orderedModels = order || toAdd;
|
730
|
+
for (i = 0, l = orderedModels.length; i < l; i++) {
|
731
|
+
this.models.push(orderedModels[i]);
|
732
|
+
}
|
716
733
|
}
|
717
734
|
}
|
718
735
|
|
719
736
|
// Silently sort the collection if appropriate.
|
720
737
|
if (sort) this.sort({silent: true});
|
721
738
|
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
739
|
+
// Unless silenced, it's time to fire all appropriate add/sort events.
|
740
|
+
if (!options.silent) {
|
741
|
+
for (i = 0, l = toAdd.length; i < l; i++) {
|
742
|
+
(model = toAdd[i]).trigger('add', model, this, options);
|
743
|
+
}
|
744
|
+
if (sort || (order && order.length)) this.trigger('sort', this, options);
|
727
745
|
}
|
728
746
|
|
729
|
-
//
|
730
|
-
|
731
|
-
return this;
|
747
|
+
// Return the added (or merged) model (or models).
|
748
|
+
return singular ? models[0] : models;
|
732
749
|
},
|
733
750
|
|
734
751
|
// When you have more items than you want to add or remove individually,
|
@@ -738,20 +755,18 @@
|
|
738
755
|
reset: function(models, options) {
|
739
756
|
options || (options = {});
|
740
757
|
for (var i = 0, l = this.models.length; i < l; i++) {
|
741
|
-
this._removeReference(this.models[i]);
|
758
|
+
this._removeReference(this.models[i], options);
|
742
759
|
}
|
743
760
|
options.previousModels = this.models;
|
744
761
|
this._reset();
|
745
|
-
this.add(models, _.extend({silent: true}, options));
|
762
|
+
models = this.add(models, _.extend({silent: true}, options));
|
746
763
|
if (!options.silent) this.trigger('reset', this, options);
|
747
|
-
return
|
764
|
+
return models;
|
748
765
|
},
|
749
766
|
|
750
767
|
// Add a model to the end of the collection.
|
751
768
|
push: function(model, options) {
|
752
|
-
|
753
|
-
this.add(model, _.extend({at: this.length}, options));
|
754
|
-
return model;
|
769
|
+
return this.add(model, _.extend({at: this.length}, options));
|
755
770
|
},
|
756
771
|
|
757
772
|
// Remove a model from the end of the collection.
|
@@ -763,9 +778,7 @@
|
|
763
778
|
|
764
779
|
// Add a model to the beginning of the collection.
|
765
780
|
unshift: function(model, options) {
|
766
|
-
|
767
|
-
this.add(model, _.extend({at: 0}, options));
|
768
|
-
return model;
|
781
|
+
return this.add(model, _.extend({at: 0}, options));
|
769
782
|
},
|
770
783
|
|
771
784
|
// Remove a model from the beginning of the collection.
|
@@ -776,14 +789,14 @@
|
|
776
789
|
},
|
777
790
|
|
778
791
|
// Slice out a sub-array of models from the collection.
|
779
|
-
slice: function(
|
780
|
-
return this.models
|
792
|
+
slice: function() {
|
793
|
+
return slice.apply(this.models, arguments);
|
781
794
|
},
|
782
795
|
|
783
796
|
// Get a model from the set by id.
|
784
797
|
get: function(obj) {
|
785
798
|
if (obj == null) return void 0;
|
786
|
-
return this._byId[obj
|
799
|
+
return this._byId[obj] || this._byId[obj.id] || this._byId[obj.cid];
|
787
800
|
},
|
788
801
|
|
789
802
|
// Get the model at the given index.
|
@@ -827,16 +840,6 @@
|
|
827
840
|
return this;
|
828
841
|
},
|
829
842
|
|
830
|
-
// Figure out the smallest index at which a model should be inserted so as
|
831
|
-
// to maintain order.
|
832
|
-
sortedIndex: function(model, value, context) {
|
833
|
-
value || (value = this.comparator);
|
834
|
-
var iterator = _.isFunction(value) ? value : function(model) {
|
835
|
-
return model.get(value);
|
836
|
-
};
|
837
|
-
return _.sortedIndex(this.models, model, iterator, context);
|
838
|
-
},
|
839
|
-
|
840
843
|
// Pluck an attribute from each model in the collection.
|
841
844
|
pluck: function(attr) {
|
842
845
|
return _.invoke(this.models, 'get', attr);
|
@@ -869,7 +872,7 @@
|
|
869
872
|
if (!options.wait) this.add(model, options);
|
870
873
|
var collection = this;
|
871
874
|
var success = options.success;
|
872
|
-
options.success = function(resp) {
|
875
|
+
options.success = function(model, resp) {
|
873
876
|
if (options.wait) collection.add(model, options);
|
874
877
|
if (success) success(model, resp, options);
|
875
878
|
};
|
@@ -899,22 +902,27 @@
|
|
899
902
|
// Prepare a hash of attributes (or other model) to be added to this
|
900
903
|
// collection.
|
901
904
|
_prepareModel: function(attrs, options) {
|
902
|
-
if (attrs instanceof Model)
|
903
|
-
|
904
|
-
return attrs;
|
905
|
-
}
|
906
|
-
options || (options = {});
|
905
|
+
if (attrs instanceof Model) return attrs;
|
906
|
+
options = options ? _.clone(options) : {};
|
907
907
|
options.collection = this;
|
908
908
|
var model = new this.model(attrs, options);
|
909
|
-
if (!model.
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
909
|
+
if (!model.validationError) return model;
|
910
|
+
this.trigger('invalid', this, model.validationError, options);
|
911
|
+
return false;
|
912
|
+
},
|
913
|
+
|
914
|
+
// Internal method to create a model's ties to a collection.
|
915
|
+
_addReference: function(model, options) {
|
916
|
+
this._byId[model.cid] = model;
|
917
|
+
if (model.id != null) this._byId[model.id] = model;
|
918
|
+
if (!model.collection) model.collection = this;
|
919
|
+
model.on('all', this._onModelEvent, this);
|
914
920
|
},
|
915
921
|
|
916
922
|
// Internal method to sever a model's ties to a collection.
|
917
|
-
_removeReference: function(model) {
|
923
|
+
_removeReference: function(model, options) {
|
924
|
+
delete this._byId[model.id];
|
925
|
+
delete this._byId[model.cid];
|
918
926
|
if (this === model.collection) delete model.collection;
|
919
927
|
model.off('all', this._onModelEvent, this);
|
920
928
|
},
|
@@ -942,8 +950,8 @@
|
|
942
950
|
'inject', 'reduceRight', 'foldr', 'find', 'detect', 'filter', 'select',
|
943
951
|
'reject', 'every', 'all', 'some', 'any', 'include', 'contains', 'invoke',
|
944
952
|
'max', 'min', 'toArray', 'size', 'first', 'head', 'take', 'initial', 'rest',
|
945
|
-
'tail', 'drop', 'last', 'without', '
|
946
|
-
'isEmpty', 'chain'];
|
953
|
+
'tail', 'drop', 'last', 'without', 'difference', 'indexOf', 'shuffle',
|
954
|
+
'lastIndexOf', 'isEmpty', 'chain', 'sample'];
|
947
955
|
|
948
956
|
// Mix in each Underscore method as a proxy to `Collection#models`.
|
949
957
|
_.each(methods, function(method) {
|
@@ -955,7 +963,7 @@
|
|
955
963
|
});
|
956
964
|
|
957
965
|
// Underscore methods that take a property name as an argument.
|
958
|
-
var attributeMethods = ['groupBy', 'countBy', 'sortBy'];
|
966
|
+
var attributeMethods = ['groupBy', 'countBy', 'sortBy', 'indexBy'];
|
959
967
|
|
960
968
|
// Use attributes instead of properties.
|
961
969
|
_.each(attributeMethods, function(method) {
|
@@ -982,7 +990,8 @@
|
|
982
990
|
// if an existing element is not provided...
|
983
991
|
var View = Backbone.View = function(options) {
|
984
992
|
this.cid = _.uniqueId('view');
|
985
|
-
|
993
|
+
options || (options = {});
|
994
|
+
_.extend(this, _.pick(options, viewOptions));
|
986
995
|
this._ensureElement();
|
987
996
|
this.initialize.apply(this, arguments);
|
988
997
|
this.delegateEvents();
|
@@ -1001,7 +1010,7 @@
|
|
1001
1010
|
tagName: 'div',
|
1002
1011
|
|
1003
1012
|
// jQuery delegate for element lookup, scoped to DOM elements within the
|
1004
|
-
// current view. This should be
|
1013
|
+
// current view. This should be preferred to global lookups where possible.
|
1005
1014
|
$: function(selector) {
|
1006
1015
|
return this.$el.find(selector);
|
1007
1016
|
},
|
@@ -1041,7 +1050,7 @@
|
|
1041
1050
|
//
|
1042
1051
|
// {
|
1043
1052
|
// 'mousedown .title': 'edit',
|
1044
|
-
// 'click .button': 'save'
|
1053
|
+
// 'click .button': 'save',
|
1045
1054
|
// 'click .open': function(e) { ... }
|
1046
1055
|
// }
|
1047
1056
|
//
|
@@ -1079,16 +1088,6 @@
|
|
1079
1088
|
return this;
|
1080
1089
|
},
|
1081
1090
|
|
1082
|
-
// Performs the initial configuration of a View with a set of options.
|
1083
|
-
// Keys with special meaning *(e.g. model, collection, id, className)* are
|
1084
|
-
// attached directly to the view. See `viewOptions` for an exhaustive
|
1085
|
-
// list.
|
1086
|
-
_configure: function(options) {
|
1087
|
-
if (this.options) options = _.extend({}, _.result(this, 'options'), options);
|
1088
|
-
_.extend(this, _.pick(options, viewOptions));
|
1089
|
-
this.options = options;
|
1090
|
-
},
|
1091
|
-
|
1092
1091
|
// Ensure that the View has a DOM element to render into.
|
1093
1092
|
// If `this.el` is a string, pass it through `$()`, take the first
|
1094
1093
|
// matching element, and re-assign it to `el`. Otherwise, create
|
@@ -1174,8 +1173,7 @@
|
|
1174
1173
|
// If we're sending a `PATCH` request, and we're in an old Internet Explorer
|
1175
1174
|
// that still has ActiveX enabled by default, override jQuery to use that
|
1176
1175
|
// for XHR instead. Remove this line when jQuery supports `PATCH` on IE8.
|
1177
|
-
if (params.type === 'PATCH' &&
|
1178
|
-
!(window.external && window.external.msActiveXFilteringEnabled)) {
|
1176
|
+
if (params.type === 'PATCH' && noXhrPatch) {
|
1179
1177
|
params.xhr = function() {
|
1180
1178
|
return new ActiveXObject("Microsoft.XMLHTTP");
|
1181
1179
|
};
|
@@ -1187,6 +1185,10 @@
|
|
1187
1185
|
return xhr;
|
1188
1186
|
};
|
1189
1187
|
|
1188
|
+
var noXhrPatch =
|
1189
|
+
typeof window !== 'undefined' && !!window.ActiveXObject &&
|
1190
|
+
!(window.XMLHttpRequest && (new XMLHttpRequest).dispatchEvent);
|
1191
|
+
|
1190
1192
|
// Map from CRUD to HTTP for our default `Backbone.sync` implementation.
|
1191
1193
|
var methodMap = {
|
1192
1194
|
'create': 'POST',
|
@@ -1275,7 +1277,7 @@
|
|
1275
1277
|
_routeToRegExp: function(route) {
|
1276
1278
|
route = route.replace(escapeRegExp, '\\$&')
|
1277
1279
|
.replace(optionalParam, '(?:$1)?')
|
1278
|
-
.replace(namedParam, function(match, optional){
|
1280
|
+
.replace(namedParam, function(match, optional) {
|
1279
1281
|
return optional ? match : '([^\/]+)';
|
1280
1282
|
})
|
1281
1283
|
.replace(splatParam, '(.*?)');
|
@@ -1325,6 +1327,9 @@
|
|
1325
1327
|
// Cached regex for removing a trailing slash.
|
1326
1328
|
var trailingSlash = /\/$/;
|
1327
1329
|
|
1330
|
+
// Cached regex for stripping urls of hash and query.
|
1331
|
+
var pathStripper = /[?#].*$/;
|
1332
|
+
|
1328
1333
|
// Has the history handling already been started?
|
1329
1334
|
History.started = false;
|
1330
1335
|
|
@@ -1349,7 +1354,7 @@
|
|
1349
1354
|
if (this._hasPushState || !this._wantsHashChange || forcePushState) {
|
1350
1355
|
fragment = this.location.pathname;
|
1351
1356
|
var root = this.root.replace(trailingSlash, '');
|
1352
|
-
if (!fragment.indexOf(root)) fragment = fragment.
|
1357
|
+
if (!fragment.indexOf(root)) fragment = fragment.slice(root.length);
|
1353
1358
|
} else {
|
1354
1359
|
fragment = this.getHash();
|
1355
1360
|
}
|
@@ -1365,7 +1370,7 @@
|
|
1365
1370
|
|
1366
1371
|
// Figure out the initial configuration. Do we need an iframe?
|
1367
1372
|
// Is pushState desired ... is it available?
|
1368
|
-
this.options = _.extend({
|
1373
|
+
this.options = _.extend({root: '/'}, this.options, options);
|
1369
1374
|
this.root = this.options.root;
|
1370
1375
|
this._wantsHashChange = this.options.hashChange !== false;
|
1371
1376
|
this._wantsPushState = !!this.options.pushState;
|
@@ -1378,7 +1383,8 @@
|
|
1378
1383
|
this.root = ('/' + this.root + '/').replace(rootStripper, '/');
|
1379
1384
|
|
1380
1385
|
if (oldIE && this._wantsHashChange) {
|
1381
|
-
|
1386
|
+
var frame = Backbone.$('<iframe src="javascript:0" tabindex="-1">');
|
1387
|
+
this.iframe = frame.hide().appendTo('body')[0].contentWindow;
|
1382
1388
|
this.navigate(fragment);
|
1383
1389
|
}
|
1384
1390
|
|
@@ -1398,19 +1404,25 @@
|
|
1398
1404
|
var loc = this.location;
|
1399
1405
|
var atRoot = loc.pathname.replace(/[^\/]$/, '$&/') === this.root;
|
1400
1406
|
|
1401
|
-
//
|
1402
|
-
//
|
1403
|
-
if (this._wantsHashChange && this._wantsPushState
|
1404
|
-
|
1405
|
-
|
1406
|
-
//
|
1407
|
-
|
1407
|
+
// Transition from hashChange to pushState or vice versa if both are
|
1408
|
+
// requested.
|
1409
|
+
if (this._wantsHashChange && this._wantsPushState) {
|
1410
|
+
|
1411
|
+
// If we've started off with a route from a `pushState`-enabled
|
1412
|
+
// browser, but we're currently in a browser that doesn't support it...
|
1413
|
+
if (!this._hasPushState && !atRoot) {
|
1414
|
+
this.fragment = this.getFragment(null, true);
|
1415
|
+
this.location.replace(this.root + this.location.search + '#' + this.fragment);
|
1416
|
+
// Return immediately as browser will do redirect to new url
|
1417
|
+
return true;
|
1418
|
+
|
1419
|
+
// Or if we've started out with a hash-based route, but we're currently
|
1420
|
+
// in a browser where it could be `pushState`-based instead...
|
1421
|
+
} else if (this._hasPushState && atRoot && loc.hash) {
|
1422
|
+
this.fragment = this.getHash().replace(routeStripper, '');
|
1423
|
+
this.history.replaceState({}, document.title, this.root + this.fragment + loc.search);
|
1424
|
+
}
|
1408
1425
|
|
1409
|
-
// Or if we've started out with a hash-based route, but we're currently
|
1410
|
-
// in a browser where it could be `pushState`-based instead...
|
1411
|
-
} else if (this._wantsPushState && this._hasPushState && atRoot && loc.hash) {
|
1412
|
-
this.fragment = this.getHash().replace(routeStripper, '');
|
1413
|
-
this.history.replaceState({}, document.title, this.root + this.fragment + loc.search);
|
1414
1426
|
}
|
1415
1427
|
|
1416
1428
|
if (!this.options.silent) return this.loadUrl();
|
@@ -1439,21 +1451,20 @@
|
|
1439
1451
|
}
|
1440
1452
|
if (current === this.fragment) return false;
|
1441
1453
|
if (this.iframe) this.navigate(current);
|
1442
|
-
this.loadUrl()
|
1454
|
+
this.loadUrl();
|
1443
1455
|
},
|
1444
1456
|
|
1445
1457
|
// Attempt to load the current URL fragment. If a route succeeds with a
|
1446
1458
|
// match, returns `true`. If no defined routes matches the fragment,
|
1447
1459
|
// returns `false`.
|
1448
|
-
loadUrl: function(
|
1449
|
-
|
1450
|
-
|
1460
|
+
loadUrl: function(fragment) {
|
1461
|
+
fragment = this.fragment = this.getFragment(fragment);
|
1462
|
+
return _.any(this.handlers, function(handler) {
|
1451
1463
|
if (handler.route.test(fragment)) {
|
1452
1464
|
handler.callback(fragment);
|
1453
1465
|
return true;
|
1454
1466
|
}
|
1455
1467
|
});
|
1456
|
-
return matched;
|
1457
1468
|
},
|
1458
1469
|
|
1459
1470
|
// Save a fragment into the hash history, or replace the URL state if the
|
@@ -1465,11 +1476,18 @@
|
|
1465
1476
|
// you wish to modify the current URL without adding an entry to the history.
|
1466
1477
|
navigate: function(fragment, options) {
|
1467
1478
|
if (!History.started) return false;
|
1468
|
-
if (!options || options === true) options = {trigger: options};
|
1469
|
-
|
1479
|
+
if (!options || options === true) options = {trigger: !!options};
|
1480
|
+
|
1481
|
+
var url = this.root + (fragment = this.getFragment(fragment || ''));
|
1482
|
+
|
1483
|
+
// Strip the fragment of the query and hash for matching.
|
1484
|
+
fragment = fragment.replace(pathStripper, '');
|
1485
|
+
|
1470
1486
|
if (this.fragment === fragment) return;
|
1471
1487
|
this.fragment = fragment;
|
1472
|
-
|
1488
|
+
|
1489
|
+
// Don't include a trailing slash on the root.
|
1490
|
+
if (fragment === '' && url !== '/') url = url.slice(0, -1);
|
1473
1491
|
|
1474
1492
|
// If pushState is available, we use it to set the fragment as a real URL.
|
1475
1493
|
if (this._hasPushState) {
|
@@ -1492,7 +1510,7 @@
|
|
1492
1510
|
} else {
|
1493
1511
|
return this.location.assign(url);
|
1494
1512
|
}
|
1495
|
-
if (options.trigger) this.loadUrl(fragment);
|
1513
|
+
if (options.trigger) return this.loadUrl(fragment);
|
1496
1514
|
},
|
1497
1515
|
|
1498
1516
|
// Update the hash location, either replacing the current entry, or adding
|
@@ -1560,7 +1578,7 @@
|
|
1560
1578
|
};
|
1561
1579
|
|
1562
1580
|
// Wrap an optional error callback with a fallback error event.
|
1563
|
-
var wrapError = function
|
1581
|
+
var wrapError = function(model, options) {
|
1564
1582
|
var error = options.error;
|
1565
1583
|
options.error = function(resp) {
|
1566
1584
|
if (error) error(model, resp, options);
|
@@ -1568,4 +1586,6 @@
|
|
1568
1586
|
};
|
1569
1587
|
};
|
1570
1588
|
|
1571
|
-
|
1589
|
+
return Backbone;
|
1590
|
+
|
1591
|
+
}));
|