Code coverage report for src/marionette.region.js

Statements: 98.46% (64 / 65)      Branches: 95.74% (45 / 47)      Functions: 100% (10 / 10)      Lines: 98.39% (61 / 62)     

All files » src/ » marionette.region.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194            1 129   129   129 1 1 1     128 1 1               1                                 96 96 96 96   96       96       96 22     96 72         96 2     96 84     96 10       96                   96   59 34 34 33   34       96               1                 80   80   80   80 74     80   80 77     80   80 80       85 75             41           74           136 136     38 22   38   38               3               17 17         1  
// Region 
// ------
//
// Manage the visual regions of your composite application. See
// http://lostechies.com/derickbailey/2011/12/12/composite-js-apps-regions-and-region-managers/
 
Marionette.Region = function(options){
  this.options = options || {};
 
  this.el = Marionette.getOption(this, "el");
 
  if (!this.el){
    var err = new Error("An 'el' must be specified for a region.");
    err.name = "NoElError";
    throw err;
  }
 
  if (this.initialize){
    var args = Array.prototype.slice.apply(arguments);
    this.initialize.apply(this, args);
  }
};
 
 
// Region Type methods
// -------------------
 
_.extend(Marionette.Region, {
 
  // Build an instance of a region by passing in a configuration object
  // and a default region type to use if none is specified in the config.
  //
  // The config object should either be a string as a jQuery DOM selector,
  // a Region type directly, or an object literal that specifies both
  // a selector and regionType:
  //
  // ```js
  // {
  //   selector: "#foo",
  //   regionType: MyCustomRegion
  // }
  // ```
  //
  buildRegion: function(regionConfig, defaultRegionType){
    var regionIsString = (typeof regionConfig === "string");
    var regionSelectorIsString = (typeof regionConfig.selector === "string");
    var regionTypeIsUndefined = (typeof regionConfig.regionType === "undefined");
    var regionIsType = (typeof regionConfig === "function");
 
    Iif (!regionIsType && !regionIsString && !regionSelectorIsString) {
      throw new Error("Region must be specified as a Region type, a selector string or an object with selector property");
    }
 
    var selector, RegionType;
   
    // get the selector for the region
    
    if (regionIsString) {
      selector = regionConfig;
    } 
 
    if (regionConfig.selector) {
      selector = regionConfig.selector;
    }
 
    // get the type for the region
    
    if (regionIsType){
      RegionType = regionConfig;
    }
 
    if (!regionIsType && regionTypeIsUndefined) {
      RegionType = defaultRegionType;
    }
 
    if (regionConfig.regionType) {
      RegionType = regionConfig.regionType;
    }
    
    // build the region instance
    var region = new RegionType({
      el: selector
    });
 
    // override the `getEl` function if we have a parentEl
    // this must be overridden to ensure the selector is found
    // on the first use of the region. if we try to assign the
    // region's `el` to `parentEl.find(selector)` in the object
    // literal to build the region, the element will not be
    // guaranteed to be in the DOM already, and will cause problems
    if (regionConfig.parentEl){
 
      region.getEl = function(selector) {
        var parentEl = regionConfig.parentEl;
        if (_.isFunction(parentEl)){
          parentEl = parentEl();
        }
        return parentEl.find(selector);
      };
    }
 
    return region;
  }
 
});
 
// Region Instance Methods
// -----------------------
 
_.extend(Marionette.Region.prototype, Backbone.Events, {
 
  // Displays a backbone view instance inside of the region.
  // Handles calling the `render` method for you. Reads content
  // directly from the `el` attribute. Also calls an optional
  // `onShow` and `close` method on your view, just after showing
  // or just before closing the view, respectively.
  show: function(view){
 
    this.ensureEl();
 
    var isViewClosed = view.isClosed || _.isUndefined(view.$el);
 
    var isDifferentView = view !== this.currentView;
 
    if (isDifferentView) {
      this.close();
    }
 
    view.render();
 
    if (isDifferentView || isViewClosed) {
      this.open(view);
    }
    
    this.currentView = view;
 
    Marionette.triggerMethod.call(this, "show", view);
    Marionette.triggerMethod.call(view, "show");
  },
 
  ensureEl: function(){
    if (!this.$el || this.$el.length === 0){
      this.$el = this.getEl(this.el);
    }
  },
 
  // Override this method to change how the region finds the
  // DOM element that it manages. Return a jQuery selector object.
  getEl: function(selector){
    return Marionette.$(selector);
  },
 
  // Override this method to change how the new view is
  // appended to the `$el` that the region is managing
  open: function(view){
    this.$el.empty().append(view.el);
  },
 
  // Close the current view, if there is one. If there is no
  // current view, it does nothing and returns immediately.
  close: function(){
    var view = this.currentView;
    if (!view || view.isClosed){ return; }
 
    // call 'close' or 'remove', depending on which is found
    if (view.close) { view.close(); }
    else Eif (view.remove) { view.remove(); }
 
    Marionette.triggerMethod.call(this, "close");
 
    delete this.currentView;
  },
 
  // Attach an existing view to the region. This 
  // will not call `render` or `onShow` for the new view, 
  // and will not replace the current HTML for the `el`
  // of the region.
  attachView: function(view){
    this.currentView = view;
  },
 
  // Reset the region by closing any existing view and
  // clearing out the cached `$el`. The next time a view
  // is shown via this region, the region will re-query the
  // DOM for the region's `el`.
  reset: function(){
    this.close();
    delete this.$el;
  }
});
 
// Copy the `extend` function used by Backbone's classes
Marionette.Region.extend = Marionette.extend;