PageRenderTime 4113ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/version 3.x/client/com/demonsters/debugger/MonsterDebuggerCore.as

http://monsterdebugger.googlecode.com/
ActionScript | 849 lines | 687 code | 52 blank | 110 comment | 46 complexity | 207463dd604be0ea5c7aa067dfd0b3f7 MD5 | raw file
Possible License(s): GPL-3.0, LGPL-3.0
  1. /**
  2. *
  3. * This is the client code that needs to be implemented into a
  4. * Flash, FLEX or AIR application to collect debug information
  5. * in De Monster Debugger.
  6. *
  7. * Be aware that any traces made to De Monster Debugger may
  8. * be viewed by others. De MonsterDebugger is intended to be
  9. * used to debug Flash, FLEX or AIR applications in a protective
  10. * environment that they will not be used in the final launch.
  11. * Please make sure that you do not send any debug material to
  12. * the debugger from a live running application.
  13. *
  14. * Use at your own risk.
  15. *
  16. * @author Ferdi Koomen, Joost Harts and Stijn van der Laan
  17. * @company De Monsters
  18. * @link http://www.MonsterDebugger.com
  19. * @version 3.02
  20. *
  21. *
  22. * Special thanks to:
  23. * Arjan van Wijk and Thijs Broerse for their feedback on the 2.5 version
  24. * Michel Wacker for sharing his P2P AIRborne library
  25. *
  26. *
  27. * Copyright 2011, De Monsters
  28. *
  29. * This program is free software: you can redistribute it and/or modify
  30. * it under the terms of the GNU Lesser General Public License as published by
  31. * the Free Software Foundation, either version 3 of the License, or
  32. * (at your option) any later version.
  33. *
  34. * This program is distributed in the hope that it will be useful,
  35. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  36. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  37. * GNU Lesser General Public License for more details.
  38. *
  39. * You should have received a copy of the GNU Lesser General Public License
  40. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  41. *
  42. */
  43. package com.demonsters.debugger
  44. {
  45. import flash.display.BitmapData;
  46. import flash.display.DisplayObject;
  47. import flash.display.Sprite;
  48. import flash.display.Stage;
  49. import flash.events.Event;
  50. import flash.events.MouseEvent;
  51. import flash.events.TimerEvent;
  52. import flash.external.ExternalInterface;
  53. import flash.geom.Point;
  54. import flash.geom.Rectangle;
  55. import flash.system.Capabilities;
  56. import flash.text.TextField;
  57. import flash.text.TextFieldAutoSize;
  58. import flash.text.TextFormat;
  59. import flash.utils.ByteArray;
  60. import flash.utils.Timer;
  61. import flash.utils.getDefinitionByName;
  62. /**
  63. * @private
  64. * The Monster Debugger core functions
  65. */
  66. internal class MonsterDebuggerCore
  67. {
  68. // Monitor and highlight interval timer
  69. private static const MONITOR_UPDATE:int = 1000;
  70. private static const HIGHLITE_COLOR:uint = 0x3399FF;
  71. // Monitor timer
  72. private static var _monitorTimer:Timer;
  73. private static var _monitorSprite:Sprite;
  74. private static var _monitorTime:Number;
  75. private static var _monitorStart:Number;
  76. private static var _monitorFrames:int;
  77. // The root of the application
  78. private static var _base:Object = null;
  79. // The stage needed for highlight
  80. private static var _stage:Stage = null;
  81. // Highlight sprite
  82. private static var _highlight:Sprite;
  83. private static var _highlightInfo:TextField;
  84. private static var _highlightTarget:DisplayObject;
  85. private static var _highlightMouse:Boolean;
  86. private static var _highlightUpdate:Boolean;
  87. // The core id
  88. internal static const ID:String = "com.demonsters.debugger.core";
  89. /**
  90. * Start the class.
  91. */
  92. internal static function initialize():void
  93. {
  94. // Reset the monitor values
  95. _monitorTime = new Date().time;
  96. _monitorStart = new Date().time;
  97. _monitorFrames = 0;
  98. // Create the monitor timer
  99. _monitorTimer = new Timer(MONITOR_UPDATE);
  100. _monitorTimer.addEventListener(TimerEvent.TIMER, monitorTimerCallback, false, 0, true);
  101. _monitorTimer.start();
  102. // Regular check for stage
  103. if (_base.hasOwnProperty("stage") && _base["stage"] != null && _base["stage"] is Stage) {
  104. _stage = _base["stage"] as Stage;
  105. }
  106. // Create the monitor sprite
  107. // This is needed for the enterframe ticks
  108. _monitorSprite = new Sprite();
  109. _monitorSprite.addEventListener(Event.ENTER_FRAME, frameHandler, false, 0, true);
  110. var format:TextFormat = new TextFormat();
  111. format.font = "Arial";
  112. format.color = 0xFFFFFF;
  113. format.size = 11;
  114. format.leftMargin = 5;
  115. format.rightMargin = 5;
  116. // Create the textfield for the highlight and inspect
  117. _highlightInfo = new TextField();
  118. _highlightInfo.embedFonts = false;
  119. _highlightInfo.autoSize = TextFieldAutoSize.LEFT;
  120. _highlightInfo.mouseWheelEnabled = false;
  121. _highlightInfo.mouseEnabled = false;
  122. _highlightInfo.condenseWhite = false;
  123. _highlightInfo.embedFonts = false;
  124. _highlightInfo.multiline = false;
  125. _highlightInfo.selectable = false;
  126. _highlightInfo.wordWrap = false;
  127. _highlightInfo.defaultTextFormat = format;
  128. _highlightInfo.text = "";
  129. // Create the highlight
  130. _highlight = new Sprite();
  131. _highlightMouse = false;
  132. _highlightTarget = null;
  133. _highlightUpdate = false;
  134. }
  135. /**
  136. * Getter and setter for base.
  137. */
  138. internal static function get base():* {
  139. return _base;
  140. }
  141. internal static function set base(value:*):void {
  142. _base = value;
  143. }
  144. /**
  145. * @private
  146. * See MonsterDebugger class
  147. */
  148. internal static function trace(caller:*, object:*, person:String = "", label:String = "", color:uint = 0x000000, depth:int = 5):void
  149. {
  150. if (MonsterDebugger.enabled) {
  151. // Get the object information
  152. var xml:XML = XML(MonsterDebuggerUtils.parse(object, "", 1, depth, false));
  153. // Create the data
  154. var data:Object = {
  155. command: MonsterDebuggerConstants.COMMAND_TRACE,
  156. memory: MonsterDebuggerUtils.getMemory(),
  157. date: new Date(),
  158. target: String(caller),
  159. reference: MonsterDebuggerUtils.getReferenceID(caller),
  160. xml: xml,
  161. person: person,
  162. label: label,
  163. color: color
  164. };
  165. // Send the data
  166. send(data);
  167. }
  168. }
  169. /**
  170. * @private
  171. * See MonsterDebugger class
  172. */
  173. internal static function snapshot(caller:*, object:DisplayObject, person:String = "", label:String = ""):void
  174. {
  175. if (MonsterDebugger.enabled) {
  176. // Create the bitmapdata
  177. var bitmapData:BitmapData = MonsterDebuggerUtils.snapshot(object);
  178. if (bitmapData != null)
  179. {
  180. // Write the bitmap in the bytearray
  181. var bytes:ByteArray = bitmapData.getPixels(new Rectangle(0, 0, bitmapData.width, bitmapData.height));
  182. // Create the data
  183. var data:Object = {
  184. command: MonsterDebuggerConstants.COMMAND_SNAPSHOT,
  185. memory: MonsterDebuggerUtils.getMemory(),
  186. date: new Date(),
  187. target: String(caller),
  188. reference: MonsterDebuggerUtils.getReferenceID(caller),
  189. bytes: bytes,
  190. width: bitmapData.width,
  191. height: bitmapData.height,
  192. person: person,
  193. label: label
  194. };
  195. // Send the data
  196. send(data);
  197. }
  198. }
  199. }
  200. /**
  201. * @private
  202. * See MonsterDebugger class
  203. */
  204. internal static function breakpoint(caller:*, id:String = "breakpoint"):void
  205. {
  206. // Only break when enabled and connected
  207. if (MonsterDebugger.enabled && MonsterDebuggerConnection.connected) {
  208. // Get the stacktrace
  209. var stack:XML = MonsterDebuggerUtils.stackTrace();
  210. // Create the data
  211. var data:Object = {
  212. command: MonsterDebuggerConstants.COMMAND_PAUSE,
  213. memory: MonsterDebuggerUtils.getMemory(),
  214. date: new Date(),
  215. target: String(caller),
  216. reference: MonsterDebuggerUtils.getReferenceID(caller),
  217. stack: stack,
  218. id: id
  219. };
  220. // Send the data
  221. send(data);
  222. // Try to pause the system
  223. MonsterDebuggerUtils.pause();
  224. }
  225. }
  226. /**
  227. * @private
  228. * See MonsterDebugger class
  229. */
  230. internal static function inspect(object:*):void
  231. {
  232. if (MonsterDebugger.enabled) {
  233. // Set the new root
  234. _base = object;
  235. // Get the new target
  236. var obj:* = MonsterDebuggerUtils.getObject(_base, "", 0);
  237. if (obj != null)
  238. {
  239. // Parse the new target
  240. var xml:XML = XML(MonsterDebuggerUtils.parse(obj, "", 1, 2, true));
  241. send({command:MonsterDebuggerConstants.COMMAND_BASE, xml:xml});
  242. }
  243. }
  244. }
  245. /**
  246. * @private
  247. * See MonsterDebugger class
  248. */
  249. internal static function clear():void
  250. {
  251. if (MonsterDebugger.enabled) {
  252. send({command:MonsterDebuggerConstants.COMMAND_CLEAR_TRACES});
  253. }
  254. }
  255. /**
  256. * Send the capabilities and information.
  257. * This is send after the HELLO command.
  258. */
  259. internal static function sendInformation():void
  260. {
  261. // Get basic data
  262. var playerType:String = Capabilities.playerType;
  263. var playerVersion:String = Capabilities.version;
  264. var isDebugger:Boolean = Capabilities.isDebugger;
  265. var isFlex:Boolean = false;
  266. var fileTitle:String = "";
  267. var fileLocation:String = "";
  268. // Check for Flex framework
  269. try{
  270. var UIComponentClass:* = getDefinitionByName("mx.core::UIComponent");
  271. if (UIComponentClass != null) isFlex = true;
  272. } catch (e1:Error) {}
  273. // Get the location
  274. if (_base is DisplayObject && _base.hasOwnProperty("loaderInfo")) {
  275. if (DisplayObject(_base).loaderInfo != null) {
  276. fileLocation = unescape(DisplayObject(_base).loaderInfo.url);
  277. }
  278. }
  279. if (_base.hasOwnProperty("stage")) {
  280. if (_base["stage"] != null && _base["stage"] is Stage) {
  281. fileLocation = unescape(Stage(_base["stage"]).loaderInfo.url);
  282. }
  283. }
  284. // Check for browser
  285. if (playerType == "ActiveX" || playerType == "PlugIn") {
  286. if (ExternalInterface.available) {
  287. try {
  288. var tmpLocation:String = ExternalInterface.call("window.location.href.toString");
  289. var tmpTitle:String = ExternalInterface.call("window.document.title.toString");
  290. if (tmpLocation != null) fileLocation = tmpLocation;
  291. if (tmpTitle != null) fileTitle = tmpTitle;
  292. } catch (e2:Error) {
  293. // External interface FAIL
  294. }
  295. }
  296. }
  297. // Check for Adobe AIR
  298. if (playerType == "Desktop") {
  299. try{
  300. var NativeApplicationClass:* = getDefinitionByName("flash.desktop::NativeApplication");
  301. if (NativeApplicationClass != null) {
  302. var descriptor:XML = NativeApplicationClass["nativeApplication"]["applicationDescriptor"];
  303. var ns:Namespace = descriptor.namespace();
  304. var filename:String = descriptor.ns::filename;
  305. var FileClass:* = getDefinitionByName("flash.filesystem::File");
  306. if (Capabilities.os.toLowerCase().indexOf("windows") != -1) {
  307. filename += ".exe";
  308. fileLocation = FileClass["applicationDirectory"]["resolvePath"](filename)["nativePath"];
  309. } else if (Capabilities.os.toLowerCase().indexOf("mac") != -1) {
  310. filename += ".app";
  311. fileLocation = FileClass["applicationDirectory"]["resolvePath"](filename)["nativePath"];
  312. }
  313. }
  314. } catch (e3:Error) {}
  315. }
  316. if (fileTitle == "" && fileLocation != "") {
  317. var slash:int = Math.max(fileLocation.lastIndexOf("\\"), fileLocation.lastIndexOf("/"));
  318. if (slash != -1) {
  319. fileTitle = fileLocation.substring(slash + 1, fileLocation.lastIndexOf("."));
  320. } else {
  321. fileTitle = fileLocation;
  322. }
  323. }
  324. // Default
  325. if (fileTitle == "") {
  326. fileTitle = "Application";
  327. }
  328. // Create the data
  329. var data:Object = {
  330. command: MonsterDebuggerConstants.COMMAND_INFO,
  331. debuggerVersion: MonsterDebugger.VERSION,
  332. playerType: playerType,
  333. playerVersion: playerVersion,
  334. isDebugger: isDebugger,
  335. isFlex: isFlex,
  336. fileLocation: fileLocation,
  337. fileTitle: fileTitle
  338. };
  339. // Send the data direct
  340. send(data, true);
  341. // Start the queue after that
  342. MonsterDebuggerConnection.processQueue();
  343. }
  344. /**
  345. * Handle incoming data from the connection.
  346. * @param item: Data from the desktop application
  347. */
  348. internal static function handle(item:MonsterDebuggerData):void
  349. {
  350. if (MonsterDebugger.enabled) {
  351. // If the id is empty just return
  352. if (item.id == null || item.id == "") {
  353. return;
  354. }
  355. // Check if we should handle the call internaly
  356. if (item.id == MonsterDebuggerCore.ID) {
  357. handleInternal(item);
  358. }
  359. }
  360. }
  361. /**
  362. * Handle internal commands from the connection.
  363. * @param item: Data from the desktop application
  364. */
  365. private static function handleInternal(item:MonsterDebuggerData):void
  366. {
  367. // Vars for loop
  368. var obj:*;
  369. var xml:XML;
  370. var method:Function;
  371. // Do the actions
  372. switch(item.data["command"])
  373. {
  374. // Get the application info and start processing queue
  375. case MonsterDebuggerConstants.COMMAND_HELLO:
  376. sendInformation();
  377. break;
  378. // Get the root xml structure (object)
  379. case MonsterDebuggerConstants.COMMAND_BASE:
  380. obj = MonsterDebuggerUtils.getObject(_base, "", 0);
  381. if (obj != null) {
  382. xml = XML(MonsterDebuggerUtils.parse(obj, "", 1, 2, true));
  383. send({command:MonsterDebuggerConstants.COMMAND_BASE, xml:xml});
  384. }
  385. break;
  386. // Inspect
  387. case MonsterDebuggerConstants.COMMAND_INSPECT:
  388. obj = MonsterDebuggerUtils.getObject(_base, item.data["target"], 0);
  389. if (obj != null) {
  390. _base = obj;
  391. xml = XML(MonsterDebuggerUtils.parse(obj, "", 1, 2, true));
  392. send({command:MonsterDebuggerConstants.COMMAND_BASE, xml:xml});
  393. }
  394. break;
  395. // Return the parsed object
  396. case MonsterDebuggerConstants.COMMAND_GET_OBJECT:
  397. obj = MonsterDebuggerUtils.getObject(_base, item.data["target"], 0);
  398. if (obj != null) {
  399. xml = XML(MonsterDebuggerUtils.parse(obj, item.data["target"], 1, 2, true));
  400. send({command:MonsterDebuggerConstants.COMMAND_GET_OBJECT, xml:xml});
  401. }
  402. break;
  403. // Return a list of properties
  404. case MonsterDebuggerConstants.COMMAND_GET_PROPERTIES:
  405. obj = MonsterDebuggerUtils.getObject(_base, item.data["target"], 0);
  406. if (obj != null) {
  407. xml = XML(MonsterDebuggerUtils.parse(obj, item.data["target"], 1, 1, false));
  408. send({command:MonsterDebuggerConstants.COMMAND_GET_PROPERTIES, xml:xml});
  409. }
  410. break;
  411. // Return a list of functions
  412. case MonsterDebuggerConstants.COMMAND_GET_FUNCTIONS:
  413. obj = MonsterDebuggerUtils.getObject(_base, item.data["target"], 0);
  414. if (obj != null) {
  415. xml = XML(MonsterDebuggerUtils.parseFunctions(obj, item.data["target"]));
  416. send({command:MonsterDebuggerConstants.COMMAND_GET_FUNCTIONS, xml:xml});
  417. }
  418. break;
  419. // Adjust a property and return the value
  420. case MonsterDebuggerConstants.COMMAND_SET_PROPERTY:
  421. obj = MonsterDebuggerUtils.getObject(_base, item.data["target"], 1);
  422. if (obj != null) {
  423. try {
  424. obj[item.data["name"]] = item.data["value"];
  425. send({command:MonsterDebuggerConstants.COMMAND_SET_PROPERTY, target:item.data["target"], value:obj[item.data["name"]]});
  426. } catch (e1:Error) {
  427. //
  428. }
  429. }
  430. break;
  431. // Return a preview
  432. case MonsterDebuggerConstants.COMMAND_GET_PREVIEW:
  433. obj = MonsterDebuggerUtils.getObject(_base, item.data["target"], 0);
  434. if (obj != null && MonsterDebuggerUtils.isDisplayObject(obj)) {
  435. var displayObject:DisplayObject = obj as DisplayObject;
  436. var bitmapData:BitmapData = MonsterDebuggerUtils.snapshot(displayObject, new Rectangle(0, 0, 300, 300));
  437. if (bitmapData != null) {
  438. var bytes:ByteArray = bitmapData.getPixels(new Rectangle(0, 0, bitmapData.width, bitmapData.height));
  439. send({command:MonsterDebuggerConstants.COMMAND_GET_PREVIEW, bytes:bytes, width:bitmapData.width, height:bitmapData.height});
  440. }
  441. }
  442. break;
  443. // Call a method and return the answer
  444. case MonsterDebuggerConstants.COMMAND_CALL_METHOD:
  445. method = MonsterDebuggerUtils.getObject(_base, item.data["target"], 0);
  446. if (method != null && method is Function) {
  447. if (item.data["returnType"] == MonsterDebuggerConstants.TYPE_VOID) {
  448. method.apply(null, item.data["arguments"]);
  449. } else {
  450. try {
  451. obj = method.apply(null, item.data["arguments"]);
  452. xml = XML(MonsterDebuggerUtils.parse(obj, "", 1, 5, false));
  453. send({command:MonsterDebuggerConstants.COMMAND_CALL_METHOD, id:item.data["id"], xml:xml});
  454. } catch (e2:Error) {
  455. //
  456. }
  457. }
  458. }
  459. break;
  460. // Pause the application
  461. case MonsterDebuggerConstants.COMMAND_PAUSE:
  462. MonsterDebuggerUtils.pause();
  463. send({command:MonsterDebuggerConstants.COMMAND_PAUSE});
  464. break;
  465. // Resume the application
  466. case MonsterDebuggerConstants.COMMAND_RESUME:
  467. MonsterDebuggerUtils.resume();
  468. send({command:MonsterDebuggerConstants.COMMAND_RESUME});
  469. break;
  470. // Set the highlite on an object
  471. case MonsterDebuggerConstants.COMMAND_HIGHLIGHT:
  472. obj = MonsterDebuggerUtils.getObject(_base, item.data["target"], 0);
  473. if (obj != null && MonsterDebuggerUtils.isDisplayObject(obj)) {
  474. if (DisplayObject(obj).stage != null && DisplayObject(obj).stage is Stage) {
  475. _stage = obj["stage"];
  476. }
  477. if (_stage != null) {
  478. highlightClear();
  479. send({command:MonsterDebuggerConstants.COMMAND_STOP_HIGHLIGHT});
  480. _highlight.removeEventListener(MouseEvent.CLICK, highlightClicked);
  481. _highlight.mouseEnabled = false;
  482. _highlightTarget = DisplayObject(obj);
  483. _highlightMouse = false;
  484. _highlightUpdate = true;
  485. }
  486. }
  487. break;
  488. // Show the highlight
  489. case MonsterDebuggerConstants.COMMAND_START_HIGHLIGHT:
  490. highlightClear();
  491. _highlight.addEventListener(MouseEvent.CLICK, highlightClicked, false, 0, true);
  492. _highlight.mouseEnabled = true;
  493. _highlightTarget = null;
  494. _highlightMouse = true;
  495. _highlightUpdate = true;
  496. send({command:MonsterDebuggerConstants.COMMAND_START_HIGHLIGHT});
  497. break;
  498. // Remove the highlight
  499. case MonsterDebuggerConstants.COMMAND_STOP_HIGHLIGHT:
  500. highlightClear();
  501. _highlight.removeEventListener(MouseEvent.CLICK, highlightClicked);
  502. _highlight.mouseEnabled = false;
  503. _highlightTarget = null;
  504. _highlightMouse = false;
  505. _highlightUpdate = false;
  506. send({command:MonsterDebuggerConstants.COMMAND_STOP_HIGHLIGHT});
  507. break;
  508. }
  509. }
  510. /**
  511. * Monitor timer callback.
  512. */
  513. private static function monitorTimerCallback(event:TimerEvent):void
  514. {
  515. if (MonsterDebugger.enabled) {
  516. // Calculate the frames per second
  517. var now:Number = new Date().time;
  518. var delta:Number = now - _monitorTime;
  519. var fps:uint = _monitorFrames / delta * 1000; // Miliseconds to seconds
  520. var fpsMovie:uint = 0;
  521. if (_stage == null) {
  522. if (_base.hasOwnProperty("stage") && _base["stage"] != null && _base["stage"] is Stage){
  523. _stage = Stage(_base["stage"]);
  524. }
  525. }
  526. if (_stage != null) {
  527. fpsMovie = _stage.frameRate;
  528. }
  529. // Reset
  530. _monitorFrames = 0;
  531. _monitorTime = now;
  532. // Check if we can send the data
  533. if (MonsterDebuggerConnection.connected)
  534. {
  535. // Create the data
  536. var data:Object = {
  537. command: MonsterDebuggerConstants.COMMAND_MONITOR,
  538. memory: MonsterDebuggerUtils.getMemory(),
  539. fps: fps,
  540. fpsMovie: fpsMovie,
  541. time: now
  542. };
  543. // Send the data
  544. send(data);
  545. }
  546. }
  547. }
  548. /**
  549. * Enterframe ticker callback.
  550. */
  551. private static function frameHandler(event:Event):void {
  552. if (MonsterDebugger.enabled) {
  553. _monitorFrames++;
  554. if (_highlightUpdate) {
  555. highlightUpdate();
  556. }
  557. }
  558. }
  559. /**
  560. * Highlight clicked.
  561. */
  562. private static function highlightClicked(event:MouseEvent):void
  563. {
  564. // Stop
  565. event.preventDefault();
  566. event.stopImmediatePropagation();
  567. // Clear the highlight
  568. highlightClear();
  569. // Get objects under point
  570. _highlightTarget = MonsterDebuggerUtils.getObjectUnderPoint(_stage, new Point(_stage.mouseX, _stage.mouseY));
  571. // Stop mouse interactions
  572. _highlightMouse = false;
  573. _highlight.removeEventListener(MouseEvent.CLICK, highlightClicked);
  574. _highlight.mouseEnabled = false;
  575. // Inspect
  576. if (_highlightTarget != null) {
  577. inspect(_highlightTarget);
  578. highlightDraw(false);
  579. }
  580. // Send stop
  581. send({command:MonsterDebuggerConstants.COMMAND_STOP_HIGHLIGHT});
  582. }
  583. /**
  584. * Highlight timer callback.
  585. */
  586. private static function highlightUpdate():void
  587. {
  588. // Clear the highlight
  589. highlightClear();
  590. // Mouse interactions
  591. if (_highlightMouse) {
  592. // Regular check for stage
  593. if (_base.hasOwnProperty("stage") && _base["stage"] != null && _base["stage"] is Stage) {
  594. _stage = _base["stage"] as Stage;
  595. }
  596. // Desktop check
  597. if (Capabilities.playerType == "Desktop") {
  598. var NativeApplicationClass:* = getDefinitionByName("flash.desktop::NativeApplication");
  599. if (NativeApplicationClass != null && NativeApplicationClass["nativeApplication"]["activeWindow"] != null) {
  600. _stage = NativeApplicationClass["nativeApplication"]["activeWindow"]["stage"];
  601. }
  602. }
  603. // Return if no stage is found
  604. if (_stage == null) {
  605. _highlight.removeEventListener(MouseEvent.CLICK, highlightClicked);
  606. _highlight.mouseEnabled = false;
  607. _highlightTarget = null;
  608. _highlightMouse = false;
  609. _highlightUpdate = false;
  610. return;
  611. }
  612. // Get objects under point
  613. _highlightTarget = MonsterDebuggerUtils.getObjectUnderPoint(_stage, new Point(_stage.mouseX, _stage.mouseY));
  614. if (_highlightTarget != null) {
  615. highlightDraw(true);
  616. }
  617. return;
  618. }
  619. // Only update the target
  620. if (_highlightTarget != null) {
  621. if (_highlightTarget.stage == null || _highlightTarget.parent == null) {
  622. _highlight.removeEventListener(MouseEvent.CLICK, highlightClicked);
  623. _highlight.mouseEnabled = false;
  624. _highlightTarget = null;
  625. _highlightMouse = false;
  626. _highlightUpdate = false;
  627. return;
  628. }
  629. highlightDraw(false);
  630. }
  631. }
  632. /**
  633. * Highlight an object.
  634. */
  635. private static function highlightDraw(fill:Boolean):void
  636. {
  637. // Return if needed
  638. if (_highlightTarget == null) {
  639. return;
  640. }
  641. // Get the outer bounds
  642. var boundsOuter:Rectangle = _highlightTarget.getBounds(_stage);
  643. if (_highlightTarget is Stage) {
  644. boundsOuter.x = 0;
  645. boundsOuter.y = 0;
  646. boundsOuter.width = _highlightTarget["stageWidth"];
  647. boundsOuter.height = _highlightTarget["stageHeight"];
  648. } else {
  649. boundsOuter.x = int(boundsOuter.x + 0.5);
  650. boundsOuter.y = int(boundsOuter.y + 0.5);
  651. boundsOuter.width = int(boundsOuter.width + 0.5);
  652. boundsOuter.height = int(boundsOuter.height + 0.5);
  653. }
  654. // Get the inner bounds for border
  655. var boundsInner:Rectangle = boundsOuter.clone();
  656. boundsInner.x += 2;
  657. boundsInner.y += 2;
  658. boundsInner.width -= 4;
  659. boundsInner.height -= 4;
  660. if (boundsInner.width < 0) boundsInner.width = 0;
  661. if (boundsInner.height < 0) boundsInner.height = 0;
  662. // Draw the first border
  663. _highlight.graphics.clear();
  664. _highlight.graphics.beginFill(HIGHLITE_COLOR, 1);
  665. _highlight.graphics.drawRect(boundsOuter.x, boundsOuter.y, boundsOuter.width, boundsOuter.height);
  666. _highlight.graphics.drawRect(boundsInner.x, boundsInner.y, boundsInner.width, boundsInner.height);
  667. if (fill) {
  668. _highlight.graphics.beginFill(HIGHLITE_COLOR, 0.25);
  669. _highlight.graphics.drawRect(boundsInner.x, boundsInner.y , boundsInner.width, boundsInner.height);
  670. }
  671. // Set the text
  672. if (_highlightTarget.name != null) {
  673. _highlightInfo.text = String(_highlightTarget.name) + " - " + String(MonsterDebuggerDescribeType.get(_highlightTarget).@name);
  674. } else {
  675. _highlightInfo.text = String(MonsterDebuggerDescribeType.get(_highlightTarget).@name);
  676. }
  677. // Calculate the text size
  678. var boundsText:Rectangle = new Rectangle(
  679. boundsOuter.x,
  680. boundsOuter.y - (_highlightInfo.textHeight + 3),
  681. _highlightInfo.textWidth + 15,
  682. _highlightInfo.textHeight + 5
  683. );
  684. // Check for offset values
  685. if (boundsText.y < 0) boundsText.y = boundsOuter.y + boundsOuter.height;
  686. if (boundsText.y + boundsText.height > _stage.stageHeight) boundsText.y = _stage.stageHeight - boundsText.height;
  687. if (boundsText.x < 0) boundsText.x = 0;
  688. if (boundsText.x + boundsText.width > _stage.stageWidth) boundsText.x = _stage.stageWidth - boundsText.width;
  689. // Draw text container
  690. _highlight.graphics.beginFill(HIGHLITE_COLOR, 1);
  691. _highlight.graphics.drawRect(boundsText.x, boundsText.y, boundsText.width, boundsText.height);
  692. _highlight.graphics.endFill();
  693. // Set position
  694. _highlightInfo.x = boundsText.x;
  695. _highlightInfo.y = boundsText.y;
  696. // Add the highlight to the objects parent
  697. try {
  698. _stage.addChild(_highlight);
  699. _stage.addChild(_highlightInfo);
  700. } catch(e:Error) {
  701. // clearHighlight();
  702. }
  703. }
  704. /**
  705. * Clear the highlight on a object
  706. */
  707. private static function highlightClear():void
  708. {
  709. if (_highlight != null && _highlight.parent != null) {
  710. _highlight.parent.removeChild(_highlight);
  711. _highlight.graphics.clear();
  712. _highlight.x = 0;
  713. _highlight.y = 0;
  714. }
  715. if (_highlightInfo != null && _highlightInfo.parent != null) {
  716. _highlightInfo.parent.removeChild(_highlightInfo);
  717. _highlightInfo.x = 0;
  718. _highlightInfo.y = 0;
  719. }
  720. }
  721. /**
  722. * Send data to the desktop application.
  723. * @param data: The data to send
  724. * @param direct: Use the queue or send direct (handshake)
  725. */
  726. private static function send(data:Object, direct:Boolean = false):void
  727. {
  728. if (MonsterDebugger.enabled) {
  729. MonsterDebuggerConnection.send(MonsterDebuggerCore.ID, data, direct);
  730. }
  731. }
  732. }
  733. }