1 /**
  2  * The Render Engine
  3  * NotifierComponent
  4  *
  5  * @fileoverview An extension of the logic component which efficiently
  6  *               notifies a list of recipients when events are triggered.
  7  *
  8  * @author: Brett Fattori (brettf@renderengine.com)
  9  * @author: $Author: bfattori $
 10  * @version: $Revision: 1555 $
 11  *
 12  * Copyright (c) 2011 Brett Fattori (brettf@renderengine.com)
 13  *
 14  * Permission is hereby granted, free of charge, to any person obtaining a copy
 15  * of this software and associated documentation files (the "Software"), to deal
 16  * in the Software without restriction, including without limitation the rights
 17  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 18  * copies of the Software, and to permit persons to whom the Software is
 19  * furnished to do so, subject to the following conditions:
 20  *
 21  * The above copyright notice and this permission notice shall be included in
 22  * all copies or substantial portions of the Software.
 23  *
 24  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 25  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 26  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 27  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 28  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 29  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 30  * THE SOFTWARE.
 31  *
 32  */
 33 
 34 // The class this file defines and its required classes
 35 R.Engine.define({
 36     "class":"R.components.logic.Notifier",
 37     "requires":[
 38         "R.components.Logic",
 39         "R.util.FNV1Hash",
 40         "R.struct.HashContainer"
 41     ]
 42 });
 43 
 44 /**
 45  * @class A component which notifies objects when an action occurs.  The component
 46  *        uses a subscriber model to notify an object when certain actions occur.
 47  *        This component can be used so that multiple objects could subscribe to one
 48  *        to be notified when a particular event occurs.  The objects don't have to
 49  *        exist within the same scope.
 50  *        <p/>
 51  *        For example, a host object could publish an event of type "death" that other
 52  *        hosts are listening for.  Thus, when the host dies, it can pass relevant
 53  *        information that other objects (such as a life counter) could respond to.
 54  *        Rather than the host having to actively know about other parts of the
 55  *        game world, those other objects could "listen in" on the actions of the host
 56  *        and act accordingly.
 57  *
 58  * @param name {String} The name of the component
 59  * @param [priority=1.0] {Number} The priority of the component
 60  * @extends R.components.Logic
 61  * @constructor
 62  * @description Create a notifier component
 63  */
 64 R.components.logic.Notifier = function () {
 65     "use strict";
 66     return R.components.Logic.extend(/** @scope R.components.logic.Notifier.prototype */{
 67 
 68         notifyLists:null,
 69         hasher:null,
 70 
 71         /**
 72          * @private
 73          */
 74         constructor:function (name, priority) {
 75             this.base(name, priority || 1.0);
 76             this.notifyLists = {};
 77             this.hasher = R.util.FNV1Hash.create();
 78         },
 79 
 80         /**
 81          * Destroy the component instance
 82          */
 83         destroy:function () {
 84             this.hasher.destroy();
 85             for (var n in this.notifyLists) {
 86                 this.notifyLists[n].destroy();
 87             }
 88             this.base();
 89         },
 90 
 91         /**
 92          * Releases the component back into the object pool. See {@link R.engine.PooledObject#release}
 93          * for more information.
 94          */
 95         release:function () {
 96             this.base();
 97             this.notifyLists = null;
 98             this.hasher = null;
 99         },
100 
101         /**
102          * Subscribe to the event type specified, receiving a subscriber Id in return.
103          * When the event type is posted, the specified callback will either be called in
104          * the scope of <tt>thisObj</tt>, or if <tt>thisObj</tt> is <tt>null</tt> then the
105          * scope will be this component's host object.
106          * <p/>
107          * Any object can subscribe to any other object's events.  This is a handy method
108          * to use event passing as a way to propagate actions from one object to a group
109          * of other objects.
110          *
111          * @param type {String} The type name of the event.
112          * @param fn {Function} The function to call when the event triggers.
113          * @param [thisObj] {Object} The object which will represent "this" for the callback.
114          *
115          * @return {String} A subscriber Id which can later be used to unsubscribe
116          */
117         subscribe:function (type, fn, thisObj) {
118             if (this.notifyLists[type] == null) {
119                 this.notifyLists[type] = R.struct.HashContainer.create("subscribers");
120                 ;
121             }
122 
123             // get a unique subscriber Id
124             var subId = this.hasher.updateHash(type + fn.toString() + (thisObj || this.getGameObject()).toString());
125             this.notifyLists[type].add(subId, {parent:thisObj, func:fn});
126             return subId;
127         },
128 
129         /**
130          * Unsubscribe from the event type specified.  If you only
131          * pass the event type, all subscribers will be removed for that type.
132          * Passing the optional <tt>subscriberId</tt> will unsubscribe a specific
133          * subscriber.
134          *
135          * @param type {String} The event type to unsubscribe from
136          * @param [subscriberId] {String} The subscriber Id which was returned from {@link #subscribe}
137          */
138         unsubscribe:function (type, subscriberId) {
139             if (subscriberId != null) {
140                 // Remove a specific subscriber
141                 this.notifyLists[type].remove(subscriberId);
142             } else {
143                 // Remove all subscribers for the event type
144                 this.notifyLists[type].clear();
145             }
146         },
147 
148         /**
149          * Post a message of the given type, with the event object
150          * which subscribers can act upon.  The event object is free-form and
151          * can contain anything.  The subscribers should know what to look for
152          * and how to interpret the event object being passed to them.
153          *
154          * @param type {String} The type of the event
155          * @param eventObj {Object} An object which subscribers can use
156          */
157         post:function (type, eventObj) {
158             this.notifyRecipients(type, eventObj);
159         },
160 
161         /**
162          * Run through the list of subscribers for the event type specified.
163          * Optimized for speed if the list is large.
164          * @private
165          */
166         notifyRecipients:function (type, eventObj) {
167             if (this.notifyLists[type] == null) {
168                 // No handlers for this type
169                 return;
170             }
171 
172             var s = null;
173             var scopeObj = null;
174             var host = this.getHostOject();
175             for (var itr = this.notifyLists[type].iterator(); itr.hasNext();) {
176                 s = itr.next();
177                 scopeObj = s.parent || host;
178                 s.func.call(scopeObj, eventObj);
179             }
180             itr.destroy();
181         }
182     }, /** @scope R.components.logic.Notifier.prototype */{
183         /**
184          * Get the class name of this object
185          *
186          * @return {String} "R.components.logic.Notifier"
187          */
188         getClassName:function () {
189             return "R.components.logic.Notifier";
190         }
191     });
192 }