1 /**
  2  * Stores, applies, and removes a named set of behaviors.  A behavior
  3  * is a set of event handlers that need to be connected and then
  4  * disconnected repeatedly as a group.
  5  * @constructor
  6  * @class
  7  * @param {Object} args.behaviors object containing the behaviors to be managed, as:
  8  * <pre>
  9  *     {
 10  *        behavior_name: {
 11  *          apply_on_init: true if this behavior should be applied when the manager is initialized,
 12  *          apply: function( manager_object, handles_array ) {
 13  *            // required function that returns an array of dojo event handles.  for example:
 14  *            return [
 15  *                dojo.connect(document.body, "mouseup",   this, 'rubberExecute'  ),
 16  *                dojo.connect(document.body, "mousemove", this, 'rubberMove'     )
 17  *            ];
 18  *          },
 19  *          remove: function( manager_object, handles_array ) {
 20  *              // optional function that removes the behavior.  by
 21  *              // default dojo.disconnect() is just called on each
 22  *              // of the event handles that were returned by the
 23  *              // apply function
 24  *          }
 25  *        },
 26  *        ...
 27  *     }
 28  * </pre>
 29  * @param {Object} [args.context=BehaviorManager itself] context
 30  *    (i.e. <code>this</code>) in which each of the behavior
 31  *    <code>apply()</code> and <code>remove()</code> functions will be
 32  *    called.
 33  */
 34 function BehaviorManager( args ) {
 35     this.context   = args.context;
 36     this.behaviors = args.behaviors;
 37 };
 38 
 39 /**
 40  * Apply the behaviors that have <code>apply_on_init</code> true.
 41  */
 42 BehaviorManager.prototype.initialize = function() {
 43     this.removeAll();
 44     for( var bname in this.behaviors ) {
 45         var b = this.behaviors[bname];
 46         if( b.apply_on_init ) {
 47             this.applyBehaviors( bname );
 48         }
 49     }
 50 };
 51 
 52 /**
 53  * Apply each of the behaviors named as arguments to this function.
 54  * @param {String} [...] Zero or more string behavior names to apply.
 55  */
 56 BehaviorManager.prototype.applyBehaviors = function() {
 57     dojo.forEach( arguments, function(name) {
 58         var b = this._get(name);
 59         if( !b.applied ) {
 60             b.handles = b.handles || [];
 61             b.handles = b.apply.call( this.context || this, this, b.handles );
 62             b.applied = true;
 63         }
 64     }, this);
 65 };
 66 
 67 /**
 68  * Look up a behavior by name, throw an exception if it's not there.
 69  * @private
 70  */
 71 BehaviorManager.prototype._get = function( name ) {
 72     var b = this.behaviors[name];
 73     if( !b ) throw "no behavior registed with name '"+"'name";
 74     return b;
 75 };
 76 
 77 /**
 78  * Given two behavior names, remove the first one and apply the second
 79  * one.  For convenience.
 80  */
 81 BehaviorManager.prototype.swapBehaviors = function( off, on ) {
 82     this.removeBehaviors(off);
 83     this.applyBehaviors(on);
 84 };
 85 
 86 /**
 87  * Remove each of the behaviors named as arguments to this function.
 88  * @param {String} [...] Zero or more string behavior names to remove.
 89  */
 90 BehaviorManager.prototype.removeBehaviors = function( ) {
 91     dojo.forEach( arguments, function(name) {
 92         var b = this._get(name);
 93         if( b.applied ) {
 94             var remove = b.remove || function( m, h ) {
 95                 dojo.forEach( h, dojo.disconnect, dojo );
 96             };
 97             remove.call( this.context || this, this, b.handles );
 98             b.applied = false;
 99         }
100     }, this);
101 };
102 
103 /**
104  * Remove all behaviors that are currently applied.
105  */
106 BehaviorManager.prototype.removeAll = function( ) {
107     for( var bname in this.behaviors ) {
108         this.removeBehaviors( bname );
109     }
110 };