xosDeveloper view marionette wip
[plstackapi.git] / planetstack / core / xoslib / static / js / backbone.babysitter.js
1 // 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 }));