blob: eeb92a2fd7b0f720c7fddece9b4008a2c7e08210 [file] [log] [blame]
Scott Baker7ee32a02014-07-13 09:52:15 -07001// Backbone.BabySitter
2// -------------------
3// v0.1.4
4//
5// Copyright (c)2014 Derick Bailey, Muted Solutions, LLC.
6// Distributed under MIT license
7//
8// http://github.com/marionettejs/backbone.babysitter
9
10(function(root, factory) {
11
12 if (typeof define === 'function' && define.amd) {
13 define(['backbone', 'underscore'], function(Backbone, _) {
14 return factory(Backbone, _);
15 });
16 } else if (typeof exports !== 'undefined') {
17 var Backbone = require('backbone');
18 var _ = require('underscore');
19 module.exports = factory(Backbone, _);
20 } else {
21 factory(root.Backbone, root._);
22 }
23
24}(this, function(Backbone, _) {
25 'use strict';
26
27 var previousChildViewContainer = Backbone.ChildViewContainer;
28
29 // BabySitter.ChildViewContainer
30 // -----------------------------
31 //
32 // Provide a container to store, retrieve and
33 // shut down child views.
34
35 Backbone.ChildViewContainer = (function (Backbone, _) {
36
37 // Container Constructor
38 // ---------------------
39
40 var Container = function(views){
41 this._views = {};
42 this._indexByModel = {};
43 this._indexByCustom = {};
44 this._updateLength();
45
46 _.each(views, this.add, this);
47 };
48
49 // Container Methods
50 // -----------------
51
52 _.extend(Container.prototype, {
53
54 // Add a view to this container. Stores the view
55 // by `cid` and makes it searchable by the model
56 // cid (and model itself). Optionally specify
57 // a custom key to store an retrieve the view.
58 add: function(view, customIndex){
59 var viewCid = view.cid;
60
61 // store the view
62 this._views[viewCid] = view;
63
64 // index it by model
65 if (view.model){
66 this._indexByModel[view.model.cid] = viewCid;
67 }
68
69 // index by custom
70 if (customIndex){
71 this._indexByCustom[customIndex] = viewCid;
72 }
73
74 this._updateLength();
75 return this;
76 },
77
78 // Find a view by the model that was attached to
79 // it. Uses the model's `cid` to find it.
80 findByModel: function(model){
81 return this.findByModelCid(model.cid);
82 },
83
84 // Find a view by the `cid` of the model that was attached to
85 // it. Uses the model's `cid` to find the view `cid` and
86 // retrieve the view using it.
87 findByModelCid: function(modelCid){
88 var viewCid = this._indexByModel[modelCid];
89 return this.findByCid(viewCid);
90 },
91
92 // Find a view by a custom indexer.
93 findByCustom: function(index){
94 var viewCid = this._indexByCustom[index];
95 return this.findByCid(viewCid);
96 },
97
98 // Find by index. This is not guaranteed to be a
99 // stable index.
100 findByIndex: function(index){
101 return _.values(this._views)[index];
102 },
103
104 // retrieve a view by its `cid` directly
105 findByCid: function(cid){
106 return this._views[cid];
107 },
108
109 // Remove a view
110 remove: function(view){
111 var viewCid = view.cid;
112
113 // delete model index
114 if (view.model){
115 delete this._indexByModel[view.model.cid];
116 }
117
118 // delete custom index
119 _.any(this._indexByCustom, function(cid, key) {
120 if (cid === viewCid) {
121 delete this._indexByCustom[key];
122 return true;
123 }
124 }, this);
125
126 // remove the view from the container
127 delete this._views[viewCid];
128
129 // update the length
130 this._updateLength();
131 return this;
132 },
133
134 // Call a method on every view in the container,
135 // passing parameters to the call method one at a
136 // time, like `function.call`.
137 call: function(method){
138 this.apply(method, _.tail(arguments));
139 },
140
141 // Apply a method on every view in the container,
142 // passing parameters to the call method one at a
143 // time, like `function.apply`.
144 apply: function(method, args){
145 _.each(this._views, function(view){
146 if (_.isFunction(view[method])){
147 view[method].apply(view, args || []);
148 }
149 });
150 },
151
152 // Update the `.length` attribute on this container
153 _updateLength: function(){
154 this.length = _.size(this._views);
155 }
156 });
157
158 // Borrowing this code from Backbone.Collection:
159 // http://backbonejs.org/docs/backbone.html#section-106
160 //
161 // Mix in methods from Underscore, for iteration, and other
162 // collection related features.
163 var methods = ['forEach', 'each', 'map', 'find', 'detect', 'filter',
164 'select', 'reject', 'every', 'all', 'some', 'any', 'include',
165 'contains', 'invoke', 'toArray', 'first', 'initial', 'rest',
166 'last', 'without', 'isEmpty', 'pluck'];
167
168 _.each(methods, function(method) {
169 Container.prototype[method] = function() {
170 var views = _.values(this._views);
171 var args = [views].concat(_.toArray(arguments));
172 return _[method].apply(_, args);
173 };
174 });
175
176 // return the public API
177 return Container;
178 })(Backbone, _);
179
180
181 Backbone.ChildViewContainer.VERSION = '0.1.4';
182
183 Backbone.ChildViewContainer.noConflict = function () {
184 Backbone.ChildViewContainer = previousChildViewContainer;
185 return this;
186 };
187
188 return Backbone.ChildViewContainer;
189
190}));