text: // Marionette.View
  // ---------------
  
  // The core view type that other Marionette views extend from.
  Marionette.View = Backbone.View.extend({
  
    constructor: function(options){
      _.bindAll(this, "render");
  
      var args = Array.prototype.slice.apply(arguments);
      Backbone.View.prototype.constructor.apply(this, args);
      this.options = options;
  
      Marionette.MonitorDOMRefresh(this);
      this.listenTo(this, "show", this.onShowCalled, this);
    },
  
    // import the "triggerMethod" to trigger events with corresponding
    // methods if the method exists
    triggerMethod: Marionette.triggerMethod,
  
    // Get the template for this view
    // instance. You can set a `template` attribute in the view
    // definition or pass a `template: "whatever"` parameter in
    // to the constructor options.
    getTemplate: function(){
      return Marionette.getOption(this, "template");
    },
  
    // Mix in template helper methods. Looks for a
    // `templateHelpers` attribute, which can either be an
    // object literal, or a function that returns an object
    // literal. All methods and attributes from this object
    // are copies to the object passed in.
    mixinTemplateHelpers: function(target){
      target = target || {};
      var templateHelpers = Marionette.getOption(this, "templateHelpers");
      if (_.isFunction(templateHelpers)){
        templateHelpers = templateHelpers.call(this);
      }
      return _.extend(target, templateHelpers);
    },
  
    // Configure `triggers` to forward DOM events to view
    // events. `triggers: {"click .foo": "do:foo"}`
    configureTriggers: function(){
      if (!this.triggers) { return; }
  
      var triggerEvents = {};
  
      // Allow `triggers` to be configured as a function
      var triggers = _.result(this, "triggers");
  
      // Configure the triggers, prevent default
      // action and stop propagation of DOM events
      _.each(triggers, function(value, key){
  
        var hasOptions = _.isObject(value);
        var eventName = hasOptions ? value.event : value;
  
        // build the event handler function for the DOM event
        triggerEvents[key] = function(e){
  
          // stop the event in its tracks
          if (e) {
            var prevent = e.preventDefault;
            var stop = e.stopPropagation;
  
            var shouldPrevent = hasOptions ? value.preventDefault : prevent;
            var shouldStop = hasOptions ? value.stopPropagation : stop;
  
            if (shouldPrevent && prevent) { prevent(); }
            if (shouldStop && stop) { stop(); }
          }
  
          // build the args for the event
          var args = {
            view: this,
            model: this.model,
            collection: this.collection
          };
  
          // trigger the event
          this.triggerMethod(eventName, args);
        };
  
      }, this);
  
      return triggerEvents;
    },
  
    // Overriding Backbone.View's delegateEvents to handle
    // the `triggers`, `modelEvents`, and `collectionEvents` configuration
    delegateEvents: function(events){
      this._delegateDOMEvents(events);
      Marionette.bindEntityEvents(this, this.model, Marionette.getOption(this, "modelEvents"));
      Marionette.bindEntityEvents(this, this.collection, Marionette.getOption(this, "collectionEvents"));
    },
  
    // internal method to delegate DOM events and triggers
    _delegateDOMEvents: function(events){
      events = events || this.events;
      if (_.isFunction(events)){ events = events.call(this); }
  
      var combinedEvents = {};
      var triggers = this.configureTriggers();
      _.extend(combinedEvents, events, triggers);
  
      Backbone.View.prototype.delegateEvents.call(this, combinedEvents);
    },
  
    // Overriding Backbone.View's undelegateEvents to handle unbinding
    // the `triggers`, `modelEvents`, and `collectionEvents` config
    undelegateEvents: function(){
      var args = Array.prototype.slice.call(arguments);
      Backbone.View.prototype.undelegateEvents.apply(this, args);
  
      Marionette.unbindEntityEvents(this, this.model, Marionette.getOption(this, "modelEvents"));
      Marionette.unbindEntityEvents(this, this.collection, Marionette.getOption(this, "collectionEvents"));
    },
  
    // Internal method, handles the `show` event.
    onShowCalled: function(){},
  
    // Default `close` implementation, for removing a view from the
    // DOM and unbinding it. Regions will call this method
    // for you. You can specify an `onClose` method in your view to
    // add custom code that is called after the view is closed.
    close: function(){
      if (this.isClosed) { return; }
  
      // allow the close to be stopped by returning `false`
      // from the `onBeforeClose` method
      var shouldClose = this.triggerMethod("before:close");
      if (shouldClose === false){
        return;
      }
  
      // mark as closed before doing the actual close, to
      // prevent infinite loops within "close" event handlers
      // that are trying to close other views
      this.isClosed = true;
      this.triggerMethod("close");
  
      // unbind UI elements
      this.unbindUIElements();
  
      // remove the view from the DOM
      this.remove();
    },
  
    // This method binds the elements specified in the "ui" hash inside the view's code with
    // the associated jQuery selectors.
    bindUIElements: function(){
      if (!this.ui) { return; }
  
      // store the ui hash in _uiBindings so they can be reset later
      // and so re-rendering the view will be able to find the bindings
      if (!this._uiBindings){
        this._uiBindings = this.ui;
      }
  
      // get the bindings result, as a function or otherwise
      var bindings = _.result(this, "_uiBindings");
  
      // empty the ui so we don't have anything to start with
      this.ui = {};
  
      // bind each of the selectors
      _.each(_.keys(bindings), function(key) {
        var selector = bindings[key];
        this.ui[key] = this.$(selector);
      }, this);
    },
  
    // This method unbinds the elements specified in the "ui" hash
    unbindUIElements: function(){
      if (!this.ui || !this._uiBindings){ return; }
  
      // delete all of the existing ui bindings
      _.each(this.ui, function($el, name){
        delete this.ui[name];
      }, this);
  
      // reset the ui element to the original bindings configuration
      this.ui = this._uiBindings;
      delete this._uiBindings;
    }
  });
  