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 }