1 /* 2 * Copyright the original author or authors. 3 * 4 * Licensed under the MOZILLA PUBLIC LICENSE, Version 1.1 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.mozilla.org/MPL/MPL-1.1.html 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 import org.as2lib.app.exec.Call; 18 import org.as2lib.env.event.impulse.AbstractImpulse; 19 import org.as2lib.env.event.impulse.Impulse; 20 import org.as2lib.env.event.impulse.FrameImpulseListener; 21 import org.as2lib.env.except.FatalException; 22 import org.as2lib.env.except.IllegalArgumentException; 23 import org.as2lib.env.reflect.ReflectUtil; 24 import org.as2lib.util.ArrayUtil; 25 26 /** 27 * {@code FrameImpulse} is a implementation of {@link Impulse} for a impulse 28 * that gets executed at a the Frame {@code onEnterFrame} event. 29 * 30 * <p>{@code FrameImpulse} supports static methods for easy connecting to a 31 * FrameImpulse. 32 * 33 * Note: Those methods can not be named in the same way as the public methods 34 * are named because of a restriction in Macromedias compiler. 35 * 36 * Example: 37 * <code> 38 * import org.as2lib.app.exec.Executable; 39 * import org.as2lib.env.event.impulse.FrameImpulseListener; 40 * import org.as2lib.app.exec.FrameImpulse; 41 * 42 * class com.domain.FrameTracer implements FrameImpulseListener { 43 * 44 * private var prefix:String; 45 * 46 * private var postfix:String; 47 * 48 * public function FrameTracer(prefix:String, postfix:String) { 49 * this.prefix = prefix; 50 * this.postfix = postfix; 51 * FrameImpulse.getInstance().addFrameImpulseListener(this); 52 * } 53 * 54 * public function onFrameImpulse(impulse:FrameImpulse):Void { 55 * trace(prefix+_root._currentframe+postfix); 56 * } 57 * } 58 * 59 * </code> 60 * 61 * @author Martin Heidegger 62 * @version 1.5 63 */ 64 class org.as2lib.env.event.impulse.FrameImpulse extends AbstractImpulse implements Impulse { 65 66 /** Holder for the static instance */ 67 private static var instance:FrameImpulse; 68 69 /** 70 * Getter for a instance of a FrameImpulse. 71 * 72 * <p>Generates a new FrameImpulse if no FrameImpulse has been set. 73 * 74 * @return {@code FrameImpulse} instance. 75 */ 76 public static function getInstance(Void):FrameImpulse { 77 if(!instance) instance = new FrameImpulse(); 78 return instance; 79 } 80 81 /** Holder for the timeline to the FrameImpulse */ 82 private var timeline:MovieClip; 83 84 /** 85 * Flag if the timeline is generated and should be destroyed after 86 * replacement. 87 */ 88 private var timelineIsGenerated:Boolean; 89 90 /** Broadcaster for connected FrameImpulseListener's */ 91 private var frameImpulseBroadcaster:Object; 92 93 /** 94 * Creates a new FrameImpulse instance. 95 * 96 * @param timeline Timeline to be used - see: {@link #setTimeline} 97 */ 98 private function FrameImpulse(timeline:MovieClip) { 99 frameImpulseBroadcaster = new Object(); 100 AsBroadcaster.initialize(frameImpulseBroadcaster); 101 setTimeline(timeline); 102 } 103 104 /** 105 * Sets a new Timeline as main timeline for the MovieClip. 106 * 107 * @param timeline Timeline to be used for the frame event. 108 * @throws IllegalArgumentException if onEnterFrame has already been used in the timeline. 109 */ 110 public function setTimeline(timeline:MovieClip):Void { 111 var e:Object = execBroadcaster; 112 var i:Object = impulseBroadcaster; 113 var f:Object = frameImpulseBroadcaster; 114 var that:Impulse = this; 115 if (timeline != null) { 116 if (timeline.onEnterFrame === undefined) { 117 118 if (this.timeline) { 119 if(timelineIsGenerated) { 120 this.timeline.removeMovieClip(); 121 } 122 delete this.timeline.onEnterFrame; 123 timelineIsGenerated = false; 124 } 125 126 this.timeline = timeline; 127 timeline.onEnterFrame = function() { 128 e.broadcastMessage("execute", that); 129 i.broadcastMessage("onImpulse", that); 130 f.broadcastMessage("onFrameImpulse", that); 131 }; 132 } else { 133 throw new IllegalArgumentException("onEnterFrame method in " 134 +timeline 135 +" has already been overwritten, its not possible to use it as Timeline for a FrameImpulse", 136 this, 137 arguments); 138 } 139 } else { 140 timeline = null; 141 getTimeline(); 142 } 143 } 144 145 /** 146 * Getter for the currently listening timeline. 147 * 148 * <p>This method creates a new timeline in root and listenes to it if no 149 * timeline has been set. 150 * 151 * @return Timeline that is currently used 152 * @throws FatalExeception if a Timeline could not be generated on the fly. 153 */ 154 public function getTimeline(Void):MovieClip { 155 if (!timeline) { 156 var name:String = ReflectUtil.getUnusedMemberName(_root); 157 if (!name) { 158 throw new FatalException("Could not get a free instance name with" 159 +" ObjectUtil.getUnusedChildName(_root)," 160 +" to create a listenercontainer.", 161 this, 162 arguments); 163 } 164 var mc:MovieClip = _root.createEmptyMovieClip(name, 165 _root.getNextHighestDepth()); 166 if (mc) { 167 setTimeline(mc); 168 } else { 169 throw new FatalException("Could not generate a timeline for " 170 +"impulse generation", this, arguments); 171 } 172 var timelineIsGenerated = true; 173 } 174 return timeline; 175 } 176 177 /** 178 * Method to add any supported listener to the FrameImpulse. 179 * 180 * <p>Adds a listener to the FrameImpulse. The listener will be informed on 181 * each frame change. 182 * 183 * <p>Example: 184 * <code> 185 * import org.as2lib.env.event.impulse.Impulse; 186 * import org.as2lib.env.event.impulse.FrameImpulse; 187 * 188 * function test(impulse:Impulse) { 189 * trace("Test called: "+impulse+" at "+getTimer()+"ms"); 190 * } 191 * 192 * var impulse:Impulse = FrameImpulse.getInstance(); 193 * impulse.addListener(new Call(this, test)); 194 * </code> 195 * 196 * <p>Note: If a certain listener implements more than one supported event it 197 * will listen to all of them at one execution (execute, onFrameImpulse, 198 * onImpulse). 199 * 200 * @param listener to be added. 201 * @throws IllegalArgumentException if the listener doesn't match any type. 202 */ 203 public function addListener(listener):Void { 204 var added:Boolean = true; 205 try { 206 super.addListener(listener); 207 } catch(e:org.as2lib.env.except.IllegalArgumentException) { 208 added = false; 209 } 210 if (listener instanceof FrameImpulseListener) { 211 frameImpulseBroadcaster.addListener(listener); 212 added = true; 213 } 214 if (!added) { 215 throw new IllegalArgumentException("Passed listener "+listener+" does not match type 'Executable', 'ImpulseListener' or 'FrameImpuseListener'", this, arguments); 216 } 217 } 218 219 /** 220 * Methode to add a {@link FrameImpulseListener} as listener to the FrameImpulse. 221 * 222 * <p>Some parts of the code get better readable if you use a complete 223 * clear name like "onFrameImpulse" to define your code. With 224 * {@code .addFrameImpulseListener} you can add a listener that specially 225 * listens only to this naming of the same event that will be executed as 226 * "onImpulse" or "execute". 227 * 228 * <p>Example: 229 * 230 * <p>Listener: 231 * <code> 232 * import org.as2lib.env.event.impulse.FrameImpulseListener; 233 * import org.as2lib.env.event.impulse.FrameImpulse; 234 * 235 * class TraceTimeImpulseListener implements FrameImpulseListener { 236 * public function onFrameImpulse(impulse:FrameImpulse):Void { 237 * trace("Frameimpulse executed at "+getTimer()); 238 * } 239 * } 240 * </code> 241 * 242 * <p>Usage: 243 * <code> 244 * import org.as2lib.env.event.impulse.FrameImpulse; 245 * 246 * var impulse:FrameImpulse = FrameImpulse.getInstance(); 247 * impulse.addFrameImpulseListener(new TraceTimeImpulseListener()); 248 * </code> 249 * 250 * @param listener Listener to be added. 251 */ 252 public function addFrameImpulseListener(listener:FrameImpulseListener):Void { 253 addListener(listener); 254 } 255 256 /** 257 * Removes a listener of any type that might be added. 258 * 259 * @param listener Listener to be removed. 260 * @throws IllegalArgumentException if you pass a listener that is of a 261 * illegal type. 262 */ 263 public function removeListener(listener):Void { 264 var notRemoved:Boolean = false; 265 try { 266 super.removeListener(listener); 267 } catch (e:org.as2lib.env.except.IllegalArgumentException) { 268 notRemoved = true; 269 } 270 if (listener instanceof FrameImpulseListener) { 271 frameImpulseBroadcaster.removeListener(listener); 272 notRemoved = false; 273 } 274 if (notRemoved) { 275 throw new IllegalArgumentException("Passed listener "+listener+" does not match type 'Executable', 'ImpulseListener' or 'FrameImpuseListener'", this, arguments); 276 } 277 } 278 279 /** 280 * Removes a {@link FrameImpulseListener} from listening to the events. 281 * 282 * <p>The passed listener will be removed from listening to any event 283 * (not only to from listening to {@code onFrameImpulse}). 284 * 285 * @param listener Listener to be removed. 286 */ 287 public function removeFrameImpulseListener(listener:FrameImpulseListener):Void { 288 removeListener(listener); 289 } 290 291 /** 292 * Getter for the list of all added listeners. 293 * 294 * <p>This method returns a list of all listeners added with eighter 295 * {@link #connectExecutable}, {@link #addListener} 296 * {@link #addImpulseListener} or {@link #addFrameImpulseListener} 297 * 298 * @return List that contains all added listeners. 299 */ 300 public function getAllListeners(Void):Array { 301 return super.getAllListeners().concat(getAllFrameImpulseListeners()); 302 } 303 304 /** 305 * Getter for the list of all added {@link FrameImpulseListener}s. 306 * 307 * @return List that contains all added {@link FrameImpulseListener}s. 308 */ 309 public function getAllFrameImpulseListeners(Void):Array { 310 return frameImpulseBroadcaster._listeners.concat(); 311 } 312 313 /** 314 * Removes all added listeners from listening to the FrameImpulse. 315 * 316 * @throws IllegalArgumentException if the 317 */ 318 public function removeAllListeners(Void):Void { 319 super.removeAllListeners(); 320 removeAllFrameImpulseListeners(); 321 } 322 323 /** 324 * Returns {@code true} if passed-in {@code listener} has been added. 325 * 326 * @param listener the listener to check whether it has been added 327 * @return {@code true} if the {@code listener} has been added 328 */ 329 public function hasListener(listener):Boolean { 330 if (hasFrameImpulseListener(listener) 331 || super.hasListener(listener)) { 332 return true; 333 } 334 return false; 335 } 336 337 /** 338 * Adds a list of {@link FrameImpulseListener}s as listener to the events. 339 * 340 * @param listeners List of all listeners to add. 341 * @throws IllegalArgumentException if one listener didn't match to any listener type. 342 * @see #addListener 343 */ 344 public function addAllFrameImpulseListeners(listeners:Array):Void { 345 for (var i:Number=0; i<listeners.length; i++) { 346 addListener(listeners[i]); 347 } 348 } 349 350 /** 351 * Removes all added {@link FrameImpulseListener}s from listening to any event. 352 */ 353 public function removeAllFrameImpulseListeners(Void):Void { 354 // As its possible that a frameimpulselistener was added as executable 355 // listener they have to be removed one by one. 356 var c:Call = new Call(this, removeListener); 357 c.forEach(frameImpulseBroadcaster._listeners); 358 } 359 360 /** 361 * Checks if a certain {@link FrameImpulseListener} has been added as 362 * listener. 363 * 364 * @param listener Listener to be checked if it has been added. 365 * @return {@code true} if the certain listener has been added. 366 */ 367 public function hasFrameImpulseListener(listener:FrameImpulseListener):Boolean { 368 return ArrayUtil.contains(frameImpulseBroadcaster._listeners, listener); 369 } 370 371 }