master
  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}));