• Jump To … +
    modules/Behavior.js modules/Cell.js modules/Collection.js modules/Events.js modules/FormModel.js modules/FormView.js modules/ListView.js modules/Model.js modules/NestedCell.js modules/NestedModel.js modules/Router.js modules/ServiceCell.js modules/View.js modules/behaviors/DataBehavior.js modules/configure.js modules/handlebarsUtils.js modules/history.js modules/mixins/cacheMixin.js modules/mixins/cellMixin.js modules/mixins/loadingMixin.js modules/mixins/modelMixin.js modules/mixins/pollingMixin.js modules/registry.js modules/stickitUtils.js modules/templateRenderer.js modules/torso.js modules/validation.js
  • Behavior.js

  • ¶
    (function(root, factory) {
      if (typeof define === 'function' && define.amd) {
        define(['underscore', './NestedCell'], factory);
      } else if (typeof exports === 'object') {
        var _ = require('underscore');
        var TorsoNestedCell = require('./NestedCell');
        module.exports = factory(_, TorsoNestedCell);
      } else {
        root.Torso = root.Torso || {};
        root.Torso.Behavior = factory(root._, root.Torso.NestedCell);
      }
    }(this, function(_, NestedCell) {
      'use strict';
  • ¶

    Map of eventName: lifecycleMethod

      var eventMap = {
        'before-attached-callback': '_attached',
        'before-detached-callback':  '_detached',
        'before-activate-callback': '_activate',
        'before-deactivate-callback': '_deactivate',
        'before-dispose-callback': '_dispose',
        'render:before-attach-tracked-views': 'attachTrackedViews',
        'render:begin': 'prerender',
        'render:complete': 'postrender',
        'initialize:begin':  'preinitialize',
        'initialize:complete': 'postinitialize'
      };
    
      var Behavior = NestedCell.extend(/** @lends Behavior# */{
        /**
         * Unique name of the behavior instance w/in a view.  More human readable than the cid.
         * @name alias
         * @type {string}
         * @memberof Behavior.prototype
         */
    
        /**
         * cidPrefix of Behaviors
         * @type {string}
         */
        cidPrefix: 'b',
    
        /**
         * Add functions to be added to the view's public API. They will be behavior-scoped.
         * @type {Object}
         */
        mixin: {},
    
        /**
         * The behavior's prepare result will be combined with the view's prepare with the behavior's alias as the namespace.
         * effectively: { [behaviorName]: behavior.prepare() } will be combined with the view's prepare result.
         *
         * @function
         * @returns {Object} a prepare context suitable to being added to the view's prepare result.
         */
        prepare: function() {
  • ¶

    do nothing, here for overrides and to properly inform jsdoc that this is a method.

        },
    
        /**
         * Allows abstraction of common view logic into separate object
         *
         * @class Behavior
         *
         * @param {Object} behaviorAttributes the initial value of the behavior's attributes.
         * @param {Object} behaviorOptions
         *   @param {external:Backbone-View} behaviorOptions.view that Behavior is attached to
         *   @param {string} behaviorOptions.alias the alias for the behavior in this view.
         * @param {Object} [viewOptions] options passed to View's initialize
         *
         * @author  deena.wang@vecna.com
         *
         * @see <a href="../annotated/modules/Behavior.html">Behavior Annotated Source</a>
         */
        constructor: function(behaviorAttributes, behaviorOptions, viewOptions) {
          behaviorOptions = behaviorOptions || {};
          if (!behaviorOptions.view) {
            throw new Error('Torso Behavior constructed without behaviorOptions.view');
          }
          this.view = behaviorOptions.view;
          if (!behaviorOptions.alias) {
            throw new Error('Torso Behavior constructed without behaviorOptions.alias');
          }
          this.alias = behaviorOptions.alias;
          this.cid = this.cid || _.uniqueId(this.cidPrefix);
          this.__bindLifecycleMethods();
          NestedCell.apply(this, arguments);
          this.__bindEventCallbacks();
        },
    
        /**
         * This is called after the view's initialize method is called and will wrap the view's prepare()
         * such that it returns the combination of the view's prepare result with the behavior's prepare result
         * inside it under the behavior's alias.
         * @private
         */
        __augmentViewPrepare: function() {
          var originalViewPrepareFn = _.bind(this.view.prepare, this.view);
          var wrappedPrepareFn = _.wrap(originalViewPrepareFn, this.__viewPrepareWrapper);
          this.view.prepare = _.bind(wrappedPrepareFn, this);
        },
    
        /**
         * Wraps the view's prepare such that it returns the combination of the view and behavior's prepare results.
         * @private
         * @param {Function} viewPrepare the prepare method from the view.
         * @returns {Object} the combined view and behavior prepare() results.
         * {
         *   <behavior alias>: behavior.prepare(),
         *   ... // view prepare properties.
         * }
         */
        __viewPrepareWrapper: function(viewPrepare) {
          var viewContext = viewPrepare() || {};
          var behaviorContext = _.omit(this.toJSON(), 'view');
          _.extend(behaviorContext, this.prepare());
          viewContext[this.alias] = behaviorContext;
          return viewContext;
        },
    
        /**
         * Registers defined lifecycle methods to be called at appropriate time in view's lifecycle
         *
         * @private
         */
        __bindLifecycleMethods: function() {
          this.listenTo(this.view, 'initialize:complete', this.__augmentViewPrepare);
          this.listenTo(this.view, 'before-dispose-callback', this.__dispose);
          _.each(eventMap, function(callback, event) {
            this.listenTo(this.view, event, this[callback]);
          }, this);
        },
    
        /**
         * Adds behavior's event handlers to view
         * Behavior's event handlers fire on view events but are run in the context of the behavior
         *
         * @private
         */
        __bindEventCallbacks: function() {
          var behaviorEvents = _.result(this, 'events');
          var viewEvents = this.view.events;
    
          if (!viewEvents) {
            if (!behaviorEvents) {
              return;
            } else {
              viewEvents = {};
            }
          }
    
          var namespacedEvents = this.__namespaceEvents(behaviorEvents);
          var boundBehaviorEvents = this.__bindEventCallbacksToBehavior(namespacedEvents);
    
          if (_.isFunction(viewEvents)) {
            this.view.events = _.wrap(_.bind(viewEvents, this.view), function(viewEventFunction) {
              return _.extend(boundBehaviorEvents, viewEventFunction());
            });
          } else if (_.isObject(viewEvents)) {
            this.view.events = _.extend(boundBehaviorEvents, viewEvents);
          }
        },
    
        /**
         * Namespaces events in event hash
         *
         * @param {Object} eventHash to namespace
         * @returns {Object} with event namespaced with '.behavior' and the cid of the behavior
         * @private
         */
        __namespaceEvents: function(eventHash) {
  • ¶

    coped from Backbone

          var delegateEventSplitter = /^(\S+)\s*(.*)$/;
          var namespacedEvents = {};
          var behaviorId = this.cid;
          _.each(eventHash, function(value, key) {
            var splitEventKey = key.match(delegateEventSplitter);
            var eventName = splitEventKey[1];
            var selector = splitEventKey[2];
            var namespacedEventName = eventName + '.behavior.' + behaviorId;
            namespacedEvents[[namespacedEventName, selector].join(' ')] = value;
          });
          return namespacedEvents;
        },
    
        /**
         * @param {Object} eventHash keys are event descriptors, values are String method names or functions
         * @returns {Object} event hash with values as methods bound to view
         * @private
         */
        __bindEventCallbacksToBehavior: function(eventHash) {
          return _.mapObject(eventHash, function(method) {
            if (!_.isFunction(method)) {
              method = this[method];
            }
            return _.bind(method, this);
          }, this);
        },
    
        /**
         * Removes all listeners, stops listening to events.
         * After dispose is called, the behavior can be safely garbage collected.
         * Called when the owning view is disposed.
         * @private
         */
        __dispose: function() {
          this.trigger('before-dispose-callback');
          this.stopListening();
          this.off();
    
          this.__isDisposed = true;
        },
    
        /**
         * Method to be invoked when dispose is called. By default calling dispose will remove the
         * behavior's on's and listenTo's.
         * Override this method to destruct any extra
         * @function
         */
        _dispose: function() {
  • ¶

    do nothing, here for overrides and to properly inform jsdoc that this is a method.

        },
    
        /**
         * @returns {boolean} true if the view was disposed
         */
        isDisposed: function() {
          return this.__isDisposed;
        }
      });
    
      return Behavior;
    }));