PageRenderTime 55ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/modules/core/export/temple/core/display/CoreSprite.as

http://templelibrary.googlecode.com/
ActionScript | 619 lines | 310 code | 69 blank | 240 comment | 40 complexity | 4a7e9c21f14284350da1878698d38845 MD5 | raw file
  1. /*
  2. * Temple Library for ActionScript 3.0
  3. * Copyright Š MediaMonks B.V.
  4. * All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without
  7. * modification, are permitted provided that the following conditions are met:
  8. * 1. Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * 2. Redistributions in binary form must reproduce the above copyright
  11. * notice, this list of conditions and the following disclaimer in the
  12. * documentation and/or other materials provided with the distribution.
  13. * 3. All advertising materials mentioning features or use of this software
  14. * must display the following acknowledgement:
  15. * This product includes software developed by MediaMonks B.V.
  16. * 4. Neither the name of MediaMonks B.V. nor the
  17. * names of its contributors may be used to endorse or promote products
  18. * derived from this software without specific prior written permission.
  19. *
  20. * THIS SOFTWARE IS PROVIDED BY MEDIAMONKS B.V. ''AS IS'' AND ANY
  21. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  22. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  23. * DISCLAIMED. IN NO EVENT SHALL MEDIAMONKS B.V. BE LIABLE FOR ANY
  24. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  25. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  26. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  27. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  28. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  29. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30. *
  31. *
  32. * Note: This license does not apply to 3rd party classes inside the Temple
  33. * repository with their own license!
  34. */
  35. package temple.core.display
  36. {
  37. import flash.display.DisplayObject;
  38. import flash.display.Loader;
  39. import flash.display.Sprite;
  40. import flash.display.Stage;
  41. import flash.events.Event;
  42. import flash.geom.Point;
  43. import temple.core.debug.Registry;
  44. import temple.core.debug.log.Log;
  45. import temple.core.debug.log.LogLevel;
  46. import temple.core.debug.objectToString;
  47. import temple.core.destruction.DestructEvent;
  48. import temple.core.destruction.Destructor;
  49. import temple.core.events.EventListenerManager;
  50. import temple.core.templelibrary;
  51. /**
  52. * @eventType temple.core.destruction.DestructEvent.DESTRUCT
  53. */
  54. [Event(name = "DestructEvent.destruct", type = "temple.core.destruction.DestructEvent")]
  55. /**
  56. * Base class for all Sprites in the Temple. The CoreSprite handles some core features of the Temple:
  57. * <ul>
  58. * <li>Registration to the Registry class.</li>
  59. * <li>Global reference to the stage trough the StageProvider.</li>
  60. * <li>Corrects a timeline bug in Flash (see <a href="http://www.tyz.nl/2009/06/23/weird-parent-thing-bug-in-flash/" target="_blank">http://www.tyz.nl/2009/06/23/weird-parent-thing-bug-in-flash/</a>).</li>
  61. * <li>Event dispatch optimization.</li>
  62. * <li>Easy remove of all EventListeners.</li>
  63. * <li>Wrapper for Log class for easy logging.</li>
  64. * <li>Completely destructible.</li>
  65. * <li>Automatic removes and destruct children, grandchildren etc. on destruction.</li>
  66. * <li>Tracked in Memory (of this feature is enabled).</li>
  67. * <li>Some useful extra properties like autoAlpha, position and scale.</li>
  68. * </ul>
  69. *
  70. * <p>You should always use and/or extend the CoreSprite instead of Sprite if you want to make use of the Temple features.</p>
  71. *
  72. * @see temple.core.Temple#registerObjectsInMemory
  73. *
  74. * @includeExample CoreDisplayObjectsExample.as
  75. *
  76. * @author Thijs Broerse
  77. */
  78. public class CoreSprite extends Sprite implements ICoreDisplayObjectContainer
  79. {
  80. /**
  81. * The current version of the Temple Library
  82. */
  83. templelibrary static const VERSION:String = "3.0.2";
  84. /**
  85. * @private
  86. *
  87. * Protected namespace for construct method. This makes overriding of constructor possible.
  88. */
  89. protected namespace construct;
  90. private const _toStringProps:Vector.<String> = Vector.<String>(['name']);
  91. private var _eventListenerManager:EventListenerManager;
  92. private var _isDestructed:Boolean;
  93. private var _onStage:Boolean;
  94. private var _onParent:Boolean;
  95. private var _registryId:uint;
  96. private var _destructOnUnload:Boolean = true;
  97. private var _emptyPropsInToString:Boolean = true;
  98. public function CoreSprite()
  99. {
  100. construct::coreSprite();
  101. }
  102. /**
  103. * @private
  104. */
  105. construct function coreSprite():void
  106. {
  107. if (this.loaderInfo) this.loaderInfo.addEventListener(Event.UNLOAD, this.handleUnload, false, 0, true);
  108. // Register object for destruction testing
  109. this._registryId = Registry.add(this);
  110. // Set listeners to keep track of object is on stage, since we can't trust the .parent property
  111. super.addEventListener(Event.ADDED, this.handleAdded);
  112. super.addEventListener(Event.ADDED_TO_STAGE, this.handleAddedToStage);
  113. super.addEventListener(Event.REMOVED, this.handleRemoved);
  114. super.addEventListener(Event.REMOVED_FROM_STAGE, this.handleRemovedFromStage);
  115. }
  116. [Temple]
  117. /**
  118. * @inheritDoc
  119. */
  120. public final function get registryId():uint
  121. {
  122. return this._registryId;
  123. }
  124. /**
  125. * Checks for a <code>scrollRect</code> and returns the width of the <code>scrollRect</code>.
  126. * Otherwise the <code>super.width</code> is returned. This fixes a FlashPlayer bug; Flash doesn't immediatly
  127. * update the objects width when a scrollRect is set on a DisplayObject.
  128. */
  129. override public function get width():Number
  130. {
  131. return this.scrollRect ? this.scrollRect.width : super.width;
  132. }
  133. /**
  134. * If the object does not have a width and is not scaled to 0 the object is empty,
  135. * setting the width is useless and can only cause weird errors, so we don't.
  136. */
  137. override public function set width(value:Number):void
  138. {
  139. if (super.width || !this.scaleX) super.width = value;
  140. }
  141. /**
  142. * Checks for a <code>scrollRect</code> and returns the height of the <code>scrollRect</code>.
  143. * Otherwise the <code>super.height</code> is returned. This fixes a FlashPlayer bug; Flash doesn't immediatly
  144. * update the objects height when a scrollRect is set on a DisplayObject.
  145. */
  146. override public function get height():Number
  147. {
  148. return this.scrollRect ? this.scrollRect.height : super.height;
  149. }
  150. /**
  151. * If the object does not have a height and is not scaled to 0 the object is empty,
  152. * setting the height is useless and can only cause weird errors, so we don't.
  153. */
  154. override public function set height(value:Number):void
  155. {
  156. if (super.height || !this.scaleY) super.height = value;
  157. }
  158. /**
  159. * When object is not on the stage it gets the stage reference from the StageProvider. So therefore this object
  160. * will always has a reference to the stage.
  161. */
  162. override public function get stage():Stage
  163. {
  164. if (!super.stage) return StageProvider.stage;
  165. return super.stage;
  166. }
  167. /**
  168. * @inheritDoc
  169. */
  170. public function get onStage():Boolean
  171. {
  172. return this._onStage;
  173. }
  174. /**
  175. * @inheritDoc
  176. */
  177. public function get hasParent():Boolean
  178. {
  179. return this._onParent;
  180. }
  181. /**
  182. * @inheritDoc
  183. */
  184. public function get autoAlpha():Number
  185. {
  186. return this.visible ? this.alpha : 0;
  187. }
  188. /**
  189. * @inheritDoc
  190. */
  191. public function set autoAlpha(value:Number):void
  192. {
  193. this.alpha = value;
  194. this.visible = this.alpha > 0;
  195. }
  196. /**
  197. * @inheritDoc
  198. */
  199. public function get position():Point
  200. {
  201. return new Point(this.x, this.y);
  202. }
  203. /**
  204. * @inheritDoc
  205. */
  206. public function set position(value:Point):void
  207. {
  208. this.x = value.x;
  209. this.y = value.y;
  210. }
  211. /**
  212. * @inheritDoc
  213. */
  214. public function get scale():Number
  215. {
  216. if (this.scaleX == this.scaleY) return this.scaleX;
  217. return NaN;
  218. }
  219. /**
  220. * @inheritDoc
  221. */
  222. public function set scale(value:Number):void
  223. {
  224. this.scaleX = this.scaleY = value;
  225. }
  226. /**
  227. * @inheritDoc
  228. */
  229. public function get destructOnUnload():Boolean
  230. {
  231. return this._destructOnUnload;
  232. }
  233. /**
  234. * @inheritDoc
  235. */
  236. public function set destructOnUnload(value:Boolean):void
  237. {
  238. this._destructOnUnload = value;
  239. }
  240. /**
  241. * @inheritDoc
  242. */
  243. public function get children():Vector.<DisplayObject>
  244. {
  245. var i:int = this.numChildren;
  246. var children:Vector.<DisplayObject> = new Vector.<DisplayObject>(i, true);
  247. for (--i; i >= 0; --i)
  248. {
  249. children[i] = this.getChildAt(i);
  250. }
  251. return children;
  252. }
  253. /**
  254. * @inheritDoc
  255. *
  256. * Check implemented if object hasEventListener, must speed up the application
  257. * http://www.gskinner.com/blog/archives/2008/12/making_dispatch.html
  258. */
  259. override public function dispatchEvent(event:Event):Boolean
  260. {
  261. if (this.hasEventListener(event.type) || event.bubbles)
  262. {
  263. return super.dispatchEvent(event);
  264. }
  265. return true;
  266. }
  267. /**
  268. * @inheritDoc
  269. */
  270. override public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void
  271. {
  272. super.addEventListener(type, listener, useCapture, priority, useWeakReference);
  273. if (this.getEventListenerManager()) this._eventListenerManager.addEventListener(type, listener, useCapture, priority, useWeakReference);
  274. }
  275. /**
  276. * @inheritDoc
  277. */
  278. public function addEventListenerOnce(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0):void
  279. {
  280. if (this.getEventListenerManager()) this._eventListenerManager.addEventListenerOnce(type, listener, useCapture, priority);
  281. }
  282. /**
  283. * @inheritDoc
  284. */
  285. override public function removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void
  286. {
  287. super.removeEventListener(type, listener, useCapture);
  288. if (this._eventListenerManager) this._eventListenerManager.removeEventListener(type, listener, useCapture);
  289. }
  290. /**
  291. * @inheritDoc
  292. */
  293. public function removeAllStrongEventListenersForType(type:String):void
  294. {
  295. if (this._eventListenerManager) this._eventListenerManager.removeAllStrongEventListenersForType(type);
  296. }
  297. /**
  298. * @inheritDoc
  299. */
  300. public function removeAllOnceEventListenersForType(type:String):void
  301. {
  302. if (this._eventListenerManager) this._eventListenerManager.removeAllOnceEventListenersForType(type);
  303. }
  304. /**
  305. * @inheritDoc
  306. */
  307. public function removeAllStrongEventListenersForListener(listener:Function):void
  308. {
  309. if (this._eventListenerManager) this._eventListenerManager.removeAllStrongEventListenersForListener(listener);
  310. }
  311. /**
  312. * @inheritDoc
  313. */
  314. public function removeAllEventListeners():void
  315. {
  316. if (this._eventListenerManager) this._eventListenerManager.removeAllEventListeners();
  317. }
  318. [Temple]
  319. /**
  320. * @inheritDoc
  321. */
  322. public function get eventListenerManager():EventListenerManager
  323. {
  324. return this._eventListenerManager;
  325. }
  326. private function getEventListenerManager():EventListenerManager
  327. {
  328. if (this._isDestructed)
  329. {
  330. this.logError("Object is destructed, don't add event listeners");
  331. return null;
  332. }
  333. return this._eventListenerManager ||= new EventListenerManager(this);
  334. }
  335. /**
  336. * Does a Log.debug, but has already filled in some known data.
  337. * @param data the data to be logged
  338. *
  339. * @see temple.core.debug.log.Log#debug()
  340. * @see temple.core.debug.log.LogLevel#DEBUG
  341. */
  342. protected final function logDebug(data:*):void
  343. {
  344. Log.templelibrary::send(data, this.toString(), LogLevel.DEBUG, this._registryId);
  345. }
  346. /**
  347. * Does a Log.error, but has already filled in some known data.
  348. * @param data the data to be logged
  349. *
  350. * @see temple.core.debug.log.Log#error()
  351. * @see temple.core.debug.log.LogLevel#ERROR
  352. */
  353. protected final function logError(data:*):void
  354. {
  355. Log.templelibrary::send(data, this.toString(), LogLevel.ERROR, this._registryId);
  356. }
  357. /**
  358. * Does a Log.fatal, but has already filled in some known data.
  359. * @param data the data to be logged
  360. *
  361. * @see temple.core.debug.log.Log#fatal()
  362. * @see temple.core.debug.log.LogLevel#FATAL
  363. */
  364. protected final function logFatal(data:*):void
  365. {
  366. Log.templelibrary::send(data, this.toString(), LogLevel.FATAL, this._registryId);
  367. }
  368. /**
  369. * Does a Log.info, but has already filled in some known data.
  370. * @param data the data to be logged
  371. *
  372. * @see temple.core.debug.log.Log#info()
  373. * @see temple.core.debug.log.LogLevel#INFO
  374. */
  375. protected final function logInfo(data:*):void
  376. {
  377. Log.templelibrary::send(data, this.toString(), LogLevel.INFO, this._registryId);
  378. }
  379. /**
  380. * Does a Log.status, but has already filled in some known data.
  381. * @param data the data to be logged
  382. *
  383. * @see temple.core.debug.log.Log#status()
  384. * @see temple.core.debug.log.LogLevel#STATUS
  385. */
  386. protected final function logStatus(data:*):void
  387. {
  388. Log.templelibrary::send(data, this.toString(), LogLevel.STATUS, this._registryId);
  389. }
  390. /**
  391. * Does a Log.warn, but has already filled in some known data.
  392. * @param data the data to be logged
  393. *
  394. * @see temple.core.debug.log.Log#warn()
  395. * @see temple.core.debug.log.LogLevel#WARN
  396. */
  397. protected final function logWarn(data:*):void
  398. {
  399. Log.templelibrary::send(data, this.toString(), LogLevel.WARN, this._registryId);
  400. }
  401. private function handleUnload(event:Event):void
  402. {
  403. if (this._destructOnUnload) this.destruct();
  404. }
  405. private function handleAdded(event:Event):void
  406. {
  407. if (event.currentTarget == this) this._onParent = true;
  408. }
  409. private function handleAddedToStage(event:Event):void
  410. {
  411. this._onStage = true;
  412. StageProvider.stage ||= super.stage;
  413. }
  414. private function handleRemoved(event:Event):void
  415. {
  416. if (event.target == this)
  417. {
  418. this._onParent = false;
  419. if (!this._isDestructed) super.addEventListener(Event.ENTER_FRAME, this.handleDestructedFrameDelay);
  420. }
  421. }
  422. private function handleDestructedFrameDelay(event:Event):void
  423. {
  424. super.removeEventListener(Event.ENTER_FRAME, this.handleDestructedFrameDelay);
  425. this.checkParent();
  426. }
  427. /**
  428. * Check objects parent, after being removed. If the object still has a parent, the object has been removed by a timeline animation.
  429. * If an object is removed by a timeline animation, the object is not used anymore and can be destructed
  430. */
  431. private function checkParent():void
  432. {
  433. if (this.parent && !this._onParent) this.destruct();
  434. }
  435. private function handleRemovedFromStage(event:Event):void
  436. {
  437. this._onStage = false;
  438. }
  439. /**
  440. * List of property names which are output in the toString() method.
  441. */
  442. protected final function get toStringProps():Vector.<String>
  443. {
  444. return this._toStringProps;
  445. }
  446. /**
  447. * @private
  448. *
  449. * Possibility to modify the toStringProps array from outside, using the templelibrary namespace.
  450. */
  451. templelibrary final function get toStringProps():Vector.<String>
  452. {
  453. return this._toStringProps;
  454. }
  455. /**
  456. * A Boolean which indicates if empty properties are output in the toString() method.
  457. */
  458. protected final function get emptyPropsInToString():Boolean
  459. {
  460. return this._emptyPropsInToString;
  461. }
  462. /**
  463. * @private
  464. */
  465. protected final function set emptyPropsInToString(value:Boolean):void
  466. {
  467. this._emptyPropsInToString = value;
  468. }
  469. /**
  470. * @private
  471. *
  472. * Possibility to modify the emptyPropsInToString value from outside, using the templelibrary namespace.
  473. */
  474. templelibrary final function get emptyPropsInToString():Boolean
  475. {
  476. return this._emptyPropsInToString;
  477. }
  478. /**
  479. * @private
  480. */
  481. templelibrary final function set emptyPropsInToString(value:Boolean):void
  482. {
  483. this._emptyPropsInToString = value;
  484. }
  485. /**
  486. * @inheritDoc
  487. */
  488. override public function toString():String
  489. {
  490. return objectToString(this, this.toStringProps, !this.emptyPropsInToString);
  491. }
  492. [Temple]
  493. /**
  494. * @inheritDoc
  495. */
  496. public final function get isDestructed():Boolean
  497. {
  498. return this._isDestructed;
  499. }
  500. /**
  501. * @inheritDoc
  502. */
  503. public function destruct():void
  504. {
  505. if (this._isDestructed) return;
  506. this.dispatchEvent(new DestructEvent(DestructEvent.DESTRUCT));
  507. // clear mask, so it won't keep a reference to an other object
  508. this.mask = null;
  509. if (this.stage && this.stage.focus == this) this.stage.focus = null;
  510. if (this.loaderInfo) this.loaderInfo.removeEventListener(Event.UNLOAD, this.handleUnload);
  511. this.removeEventListener(Event.ENTER_FRAME, this.handleDestructedFrameDelay);
  512. if (this._eventListenerManager)
  513. {
  514. this._eventListenerManager.destruct();
  515. this._eventListenerManager = null;
  516. }
  517. super.removeEventListener(Event.ADDED, this.handleAdded);
  518. super.removeEventListener(Event.ADDED_TO_STAGE, this.handleAddedToStage);
  519. super.removeEventListener(Event.REMOVED, this.handleRemoved);
  520. super.removeEventListener(Event.REMOVED_FROM_STAGE, this.handleRemovedFromStage);
  521. Destructor.destructChildren(this);
  522. if (this.parent)
  523. {
  524. if (this.parent is Loader)
  525. {
  526. Loader(this.parent).unload();
  527. }
  528. else
  529. {
  530. if (this._onParent)
  531. {
  532. if (this.name && this.parent.hasOwnProperty(this.name)) this.parent[this.name] = null;
  533. this.parent.removeChild(this);
  534. }
  535. else
  536. {
  537. // something weird happened, since we have a parent but didn't receive an ADDED event. So do the try-catch thing
  538. try
  539. {
  540. if (this.name && this.parent.hasOwnProperty(this.name)) this.parent[this.name] = null;
  541. this.parent.removeChild(this);
  542. }
  543. catch (e:Error){}
  544. }
  545. }
  546. }
  547. this._isDestructed = true;
  548. }
  549. }
  550. }