PageRenderTime 63ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/src/nl/demonsters/debugger/MonsterDebugger.as

http://github.com/FlashJunior/OSFlashVideoPlayer
ActionScript | 1914 lines | 1265 code | 244 blank | 405 comment | 317 complexity | 88b508eaa3171a1799548bcd66df6c09 MD5 | raw file
  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 MonsterDebugger.
  6. *
  7. * Be aware that any traces made to De MonsterDebugger 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
  17. * @company De Monsters
  18. * @link http://www.deMonsterDebugger.com
  19. * @version 2.5.1 (stable)
  20. *
  21. *
  22. * Special thanks to Arjan van Wijk and Thijs Broerse from MediaMonks.nl
  23. *
  24. *
  25. * Copyright 2009, De Monsters
  26. *
  27. * This program is free software: you can redistribute it and/or modify
  28. * it under the terms of the GNU Lesser General Public License as published by
  29. * the Free Software Foundation, either version 3 of the License, or
  30. * (at your option) any later version.
  31. *
  32. * This program is distributed in the hope that it will be useful,
  33. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  34. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  35. * GNU Lesser General Public License for more details.
  36. *
  37. * You should have received a copy of the GNU Lesser General Public License
  38. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  39. *
  40. */
  41. package nl.demonsters.debugger
  42. {
  43. import flash.display.BitmapData;
  44. import flash.display.DisplayObject;
  45. import flash.display.DisplayObjectContainer;
  46. import flash.display.Sprite;
  47. import flash.events.AsyncErrorEvent;
  48. import flash.events.SecurityErrorEvent;
  49. import flash.events.StatusEvent;
  50. import flash.events.TimerEvent;
  51. import flash.events.Event;
  52. import flash.net.LocalConnection;
  53. import flash.geom.Rectangle;
  54. import flash.utils.ByteArray;
  55. import flash.utils.Timer;
  56. import flash.utils.describeType;
  57. import flash.system.System;
  58. public class MonsterDebugger
  59. {
  60. // Singleton instance
  61. private static var instance:MonsterDebugger = null;
  62. // Connections
  63. private var lineOut:LocalConnection;
  64. private var lineIn:LocalConnection;
  65. // Connection names
  66. private const LINE_OUT :String = "_debuggerRed";
  67. private const LINE_IN :String = "_debuggerBlue";
  68. // The allow domain for the local connection
  69. // * = Allow communication with all domains
  70. private const ALLOWED_DOMAIN :String = "*";
  71. // Error colors
  72. public static const COLOR_NORMAL :uint = 0x111111;
  73. public static const COLOR_ERROR :uint = 0xFF0000;
  74. public static const COLOR_WARNING :uint = 0xFF3300;
  75. // Commands
  76. private const COMMAND_HELLO :String = "HELLO";
  77. private const COMMAND_HELLO_RESPONSE :String = "HELLO_RESPONSE";
  78. private const COMMAND_ROOT :String = "ROOT";
  79. private const COMMAND_BASE :String = "BASE";
  80. private const COMMAND_TRACE :String = "TRACE";
  81. private const COMMAND_INSPECT :String = "INSPECT";
  82. private const COMMAND_GET_OBJECT :String = "GET_OBJECT";
  83. private const COMMAND_GET_DISPLAYOBJECT :String = "GET_DISPLAYOBJECT";
  84. private const COMMAND_GET_PROPERTIES :String = "GET_PROPERTIES";
  85. private const COMMAND_GET_FUNCTIONS :String = "GET_FUNCTIONS";
  86. private const COMMAND_SET_PROPERTY :String = "SET_PROPERTY";
  87. private const COMMAND_CALL_METHOD :String = "CALL_METHOD";
  88. private const COMMAND_SHOW_HIGHLIGHT :String = "SHOW_HIGHLIGHT";
  89. private const COMMAND_HIDE_HIGHLIGHT :String = "HIDE_HIGHLIGHT";
  90. private const COMMAND_CLEAR_TRACES :String = "CLEAR_TRACES";
  91. private const COMMAND_MONITOR :String = "MONITOR";
  92. private const COMMAND_SNAPSHOT :String = "SNAPSHOT";
  93. private const COMMAND_NOTFOUND :String = "NOTFOUND";
  94. // Types
  95. private const TYPE_VOID :String = "void";
  96. private const TYPE_ARRAY :String = "Array";
  97. private const TYPE_BOOLEAN :String = "Boolean";
  98. private const TYPE_NUMBER :String = "Number";
  99. private const TYPE_OBJECT :String = "Object";
  100. private const TYPE_VECTOR :String = "Vector";
  101. private const TYPE_STRING :String = "String";
  102. private const TYPE_INT :String = "int";
  103. private const TYPE_UINT :String = "uint";
  104. private const TYPE_XML :String = "XML";
  105. private const TYPE_XMLLIST :String = "XMLList";
  106. private const TYPE_XMLNODE :String = "XMLNode";
  107. private const TYPE_XMLVALUE :String = "XMLValue";
  108. private const TYPE_XMLATTRIBUTE :String = "XMLAttribute";
  109. private const TYPE_METHOD :String = "MethodClosure";
  110. private const TYPE_FUNCTION :String = "Function";
  111. private const TYPE_BYTEARRAY :String = "ByteArray";
  112. private const TYPE_WARNING :String = "Warning";
  113. // Access types
  114. private const ACCESS_VARIABLE :String = "variable";
  115. private const ACCESS_CONSTANT :String = "constant";
  116. private const ACCESS_ACCESSOR :String = "accessor";
  117. private const ACCESS_METHOD :String = "method";
  118. // Permission types
  119. private const PERMISSION_READWRITE :String = "readwrite";
  120. private const PERMISSION_READONLY :String = "readonly";
  121. private const PERMISSION_WRITEONLY :String = "writeonly";
  122. // Icon types
  123. private const ICON_DEFAULT :String = "iconDefault";
  124. private const ICON_ROOT :String = "iconRoot";
  125. private const ICON_WARNING :String = "iconWarning";
  126. private const ICON_VARIABLE :String = "iconVariable";
  127. private const ICON_VARIABLE_READONLY :String = "iconVariableReadonly";
  128. private const ICON_VARIABLE_WRITEONLY :String = "iconVariableWriteonly";
  129. private const ICON_XMLNODE :String = "iconXMLNode";
  130. private const ICON_XMLVALUE :String = "iconXMLValue";
  131. private const ICON_XMLATTRIBUTE :String = "iconXMLAttribute";
  132. private const ICON_FUNCTION :String = "iconFunction";
  133. // Highlight color and border
  134. protected const HIGHLIGHT_COLOR :uint = 0xFFFF00;
  135. protected const HIGHLIGHT_BORDER :int = 4;
  136. // Max local connection package size
  137. // Max buffer size
  138. protected const MAX_PACKAGE_BYTES :int = 40000;
  139. protected const MAX_BUFFER_SIZE :int = 500;
  140. // Version
  141. protected const VERSION :Number = 2.51;
  142. // FPS interval timer
  143. protected const FPS_UPDATE :int = 500;
  144. // The root of the application
  145. protected var root:Object = null;
  146. // Highlight display object
  147. protected var highlight:Sprite = null;
  148. // Message buffer
  149. protected var buffer:Array = new Array();
  150. // Timer for the monitor
  151. protected var monitor:Timer;
  152. protected var monitorTime:Number;
  153. protected var monitorStart:Number;
  154. protected var monitorFrames:uint;
  155. protected var monitorSprite:Sprite;
  156. // Enabled / disabled
  157. protected var isEnabled:Boolean = true;
  158. protected var isConnected:Boolean = false;
  159. // Debugger for the debugger ;-)
  160. //
  161. // var debugger:MonsterDebugger = new MonsterDebugger(this);
  162. // debugger.logger = function(...args):void {
  163. // trace(args);
  164. // };
  165. public var logger:Function;
  166. /**
  167. * Constructor
  168. * The target can also be a static function
  169. * @param target: The root of the application
  170. */
  171. public function MonsterDebugger(target:Object = null)
  172. {
  173. // Check if the debugger has already been initialized
  174. if (instance == null)
  175. {
  176. // Save the instance
  177. instance = this;
  178. // Setup line out
  179. lineOut = new LocalConnection();
  180. lineOut.addEventListener(AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler, false, 0, true);
  181. lineOut.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler, false, 0, true);
  182. lineOut.addEventListener(StatusEvent.STATUS, statusHandler, false, 0, true);
  183. // Setup line in
  184. lineIn = new LocalConnection();
  185. lineIn.addEventListener(AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler, false, 0, true);
  186. lineIn.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler, false, 0, true);
  187. lineIn.addEventListener(StatusEvent.STATUS, statusHandler, false, 0, true);
  188. lineIn.allowDomain(ALLOWED_DOMAIN);
  189. lineIn.client = this;
  190. // Setup the fps and memory listeners
  191. monitorTime = new Date().time;
  192. monitorStart = new Date().time;
  193. monitorFrames = 0;
  194. monitorSprite = new Sprite();
  195. monitorSprite.addEventListener(Event.ENTER_FRAME, enterFrameHandler, false, 0, true);
  196. monitor = new Timer(FPS_UPDATE);
  197. monitor.addEventListener(TimerEvent.TIMER, monitorHandler, false, 0, true);
  198. monitor.start();
  199. try {
  200. lineIn.connect(LINE_IN);
  201. } catch(error:ArgumentError) {
  202. // Do nothing
  203. }
  204. }
  205. // Save the root
  206. // Send the first message
  207. instance.root = target;
  208. instance.send({text:COMMAND_HELLO, version:VERSION});
  209. }
  210. /**
  211. * This function is called from the AIR application
  212. * @param data: A compressed object containing the commands
  213. */
  214. public function onReceivedData(data:ByteArray):void
  215. {
  216. if (isEnabled)
  217. {
  218. //Variables for the commands
  219. var object:*;
  220. var method:Function;
  221. var xml:XML;
  222. // Uncompress the item data
  223. data.uncompress();
  224. // Read the command from the data
  225. var command:Object = data.readObject();
  226. // Do the actions
  227. switch(command["text"])
  228. {
  229. // Save the domain
  230. case COMMAND_HELLO:
  231. isConnected = true;
  232. send({text:COMMAND_HELLO, version:VERSION});
  233. break;
  234. // Send the buffer
  235. case COMMAND_HELLO_RESPONSE:
  236. isConnected = true;
  237. sendBuffer();
  238. break;
  239. // Get the base of the application
  240. case COMMAND_ROOT:
  241. object = getObject("", 0);
  242. if (object != null) {
  243. xml = XML(parseObject(object, "", command["functions"], 1, 2));
  244. send({text:COMMAND_ROOT, xml:xml});
  245. if (isDisplayObject(object)) {
  246. xml = XML(parseDisplayObject(object, "", command["functions"], 1, 2));
  247. send({text:COMMAND_BASE, xml:xml});
  248. }
  249. }
  250. break;
  251. // Return the parsed object
  252. case COMMAND_GET_OBJECT:
  253. object = getObject(command["target"], 0);
  254. if (object != null) {
  255. xml = XML(parseObject(object, command["target"], command["functions"], 1, 2));
  256. send({text:COMMAND_GET_OBJECT, xml:xml});
  257. }
  258. break;
  259. // Return the parsed object
  260. case COMMAND_GET_DISPLAYOBJECT:
  261. object = getObject(command["target"], 0);
  262. if (object != null) {
  263. if (isDisplayObject(object)) {
  264. xml = XML(parseDisplayObject(object, command["target"], command["functions"], 1, 2));
  265. send({text:COMMAND_GET_DISPLAYOBJECT, xml:xml});
  266. }
  267. }
  268. break;
  269. // Return a list of functions
  270. case COMMAND_GET_PROPERTIES:
  271. object = getObject(command["target"], 0);
  272. if (object != null) {
  273. xml = XML(parseObject(object, command["target"], false, 1, 1));
  274. send({text:COMMAND_GET_PROPERTIES, xml:xml});
  275. }
  276. break;
  277. // Return a list of functions
  278. case COMMAND_GET_FUNCTIONS:
  279. object = getObject(command["target"], 0);
  280. if (object != null) {
  281. xml = XML(getFunctions(object, command["target"]));
  282. send({text:COMMAND_GET_FUNCTIONS, xml:xml});
  283. }
  284. break;
  285. // Adjust a property and return the value
  286. case COMMAND_SET_PROPERTY:
  287. object = getObject(command["target"], 1);
  288. if (object != null) {
  289. try {
  290. object[command["name"]] = command["value"];
  291. send({text:COMMAND_SET_PROPERTY, value:object[command["name"]]});
  292. } catch (error:Error) {
  293. send({text:COMMAND_NOTFOUND, target:command["target"]});
  294. break;
  295. }
  296. }
  297. break;
  298. // Call a method and return the answer
  299. case COMMAND_CALL_METHOD:
  300. method = getObject(command["target"], 0);
  301. if (method != null) {
  302. if (command["returnType"] == TYPE_VOID) {
  303. method.apply(this, command["arguments"]);
  304. } else {
  305. object = method.apply(this, command["arguments"]);
  306. xml = XML(parseObject(object, "", false, 1, 4));
  307. send({text:COMMAND_CALL_METHOD, id:command["id"], xml:xml});
  308. }
  309. }
  310. break;
  311. // Remove and add a new highlight if posible
  312. case COMMAND_SHOW_HIGHLIGHT:
  313. if (highlight != null) {
  314. try {
  315. highlight.parent.removeChild(highlight);
  316. highlight = null;
  317. } catch(error:Error) {
  318. //
  319. }
  320. }
  321. object = getObject(command["target"], 0);
  322. if (isDisplayObject(object) && isDisplayObject(object["parent"])) {
  323. var bounds:Rectangle = object.getBounds(object["parent"]);
  324. highlight = new Sprite();
  325. highlight.x = 0;
  326. highlight.y = 0;
  327. highlight.graphics.beginFill(0, 0);
  328. highlight.graphics.lineStyle(HIGHLIGHT_BORDER, HIGHLIGHT_COLOR);
  329. highlight.graphics.drawRect(bounds.x, bounds.y, bounds.width, bounds.height);
  330. highlight.graphics.endFill();
  331. highlight.mouseChildren = false;
  332. highlight.mouseEnabled = false;
  333. try {
  334. object["parent"].addChild(highlight);
  335. } catch(error:Error) {
  336. highlight = null;
  337. }
  338. }
  339. break;
  340. // Remove the highlight
  341. case COMMAND_HIDE_HIGHLIGHT:
  342. if (highlight != null) {
  343. try {
  344. highlight.parent.removeChild(highlight);
  345. highlight = null;
  346. } catch(error:Error) {
  347. //
  348. }
  349. }
  350. break;
  351. }
  352. }
  353. }
  354. /**
  355. * The actual send function
  356. * @param data: The raw uncompressed data to send
  357. */
  358. protected function send(data:Object):void
  359. {
  360. if (isEnabled)
  361. {
  362. // Compress the data
  363. var item:ByteArray = new ByteArray();
  364. item.writeObject(data);
  365. item.compress();
  366. // Array to hold the data packages
  367. var dataPackages:Array = new Array();
  368. // Counter for the loops
  369. var i:int = 0;
  370. // Check if the data should be splitted
  371. // The max size for localconnection = 40kb = 40960b
  372. // We use 960b for the package definition
  373. if (item.length > MAX_PACKAGE_BYTES)
  374. {
  375. // Save the length
  376. var bytesAvailable:int = item.length;
  377. var offset:int = 0;
  378. // Calculate the total package count
  379. var total:int = Math.ceil(item.length / MAX_PACKAGE_BYTES);
  380. // Loop through the bytes / chunks
  381. for (i = 0; i < total; i++)
  382. {
  383. // Set the length to read
  384. var length:int = bytesAvailable;
  385. if (length > MAX_PACKAGE_BYTES) {
  386. length = MAX_PACKAGE_BYTES;
  387. }
  388. // Read a chunk of data
  389. var tmp:ByteArray = new ByteArray();
  390. tmp.writeBytes(item, offset, length);
  391. // Create a data package
  392. dataPackages.push({total:total, nr:(i + 1), bytes:tmp});
  393. // Update the bytes available and offset
  394. bytesAvailable -= length;
  395. offset += length;
  396. }
  397. }
  398. else
  399. {
  400. // The data size is under 40kb, so just send one package
  401. dataPackages.push({total:1, nr:1, bytes:item});
  402. }
  403. // send the data packages through the line out
  404. for (i = 0; i < dataPackages.length; i++) {
  405. try {
  406. lineOut.send(LINE_OUT, "onReceivedData", dataPackages[i]);
  407. } catch (error:Error) {
  408. break;
  409. }
  410. }
  411. }
  412. }
  413. /**
  414. * Send data to the buffer
  415. * @param data: The raw uncompressed data to send
  416. */
  417. protected function sendToBuffer(obj:Object):void
  418. {
  419. buffer.push(obj);
  420. if (buffer.length > MAX_BUFFER_SIZE) buffer.shift();
  421. }
  422. /**
  423. * Send the buffer to the flex app
  424. */
  425. protected function sendBuffer():void
  426. {
  427. if (buffer.length > 0) {
  428. while (buffer.length != 0) {
  429. send(buffer.shift());
  430. }
  431. }
  432. }
  433. /**
  434. * Set another target in the or window
  435. * @param target: The target to display in the or
  436. */
  437. public static function inspect(target:Object):void
  438. {
  439. // Check if the debugger has been enabled
  440. if (instance != null && target != null)
  441. {
  442. // Set the new root
  443. instance.root = target;
  444. // Parse the new target
  445. // Send the new XML
  446. var object:* = instance.getObject("", 0);
  447. if (object != null) {
  448. var xml:XML = XML(instance.parseObject(object, "", false, 1, 2));
  449. var obj:Object = {text:instance.COMMAND_INSPECT, xml:xml};
  450. if (instance.isConnected) {
  451. instance.send(obj);
  452. } else {
  453. instance.sendToBuffer(obj);
  454. }
  455. if (instance.isDisplayObject(object)) {
  456. xml = XML(instance.parseDisplayObject(object, "", false, 1, 2));
  457. obj = {text:instance.COMMAND_BASE, xml:xml};
  458. if (instance.isConnected) {
  459. instance.send(obj);
  460. } else {
  461. instance.sendToBuffer(obj);
  462. }
  463. }
  464. }
  465. }
  466. }
  467. /**
  468. * Static snapshot function
  469. * @param target: The target from where the snapshot is called
  470. * @param object: The object to snapshot
  471. * @param color: The color in the interface
  472. */
  473. public static function snapshot(target:DisplayObject, color:uint = 0x111111):void
  474. {
  475. if (instance == null) instance = new MonsterDebugger(null);
  476. if (MonsterDebugger.enabled) instance.snapshotInternal(target, color);
  477. }
  478. /**
  479. * Private snapshot function
  480. * @param target: The target from where the snapshot is called
  481. * @param object: The object to snapshot
  482. * @param color: The color in the interface
  483. */
  484. protected function snapshotInternal(target:DisplayObject, color:uint = 0x111111):void
  485. {
  486. if (isEnabled)
  487. {
  488. // Create the bitmapdata
  489. var bitmapData:BitmapData = new BitmapData(target.width, target.height);
  490. bitmapData.draw(target);
  491. // Write the bitmap in the bytearray
  492. var bytes:ByteArray = bitmapData.getPixels(new Rectangle(0, 0, target.width, target.height));
  493. // Get memory
  494. var memory:uint = System.totalMemory;
  495. //Create a send object
  496. var obj:Object = {text:COMMAND_SNAPSHOT, date:new Date(), target:String(target), bytes:bytes, width:target.width, height:target.height, color:color, memory:memory};
  497. if (isConnected) {
  498. send(obj);
  499. } else {
  500. sendToBuffer(obj);
  501. }
  502. // Clear the data
  503. bitmapData.dispose();
  504. bytes = null;
  505. }
  506. }
  507. /**
  508. * Static trace function
  509. * @param target: The target from where the trace is called
  510. * @param object: The object to trace
  511. * @param color: The color of the trace in the interface
  512. * @parem functions: Include or exclude the functions
  513. * @param depth: The maximum depth of the trace
  514. */
  515. public static function trace(target:Object, object:*, color:uint = 0x111111, functions:Boolean = false, depth:int = 4):void
  516. {
  517. if (instance == null) instance = new MonsterDebugger(target);
  518. if (MonsterDebugger.enabled) instance.traceInternal(target, object, color, functions, depth);
  519. }
  520. /**
  521. * Private trace function
  522. * @param target: The target from where the trace is called
  523. * @param object: The object to trace
  524. * @param color: The color of the trace in the interface
  525. * @parem functions: Include or exclude the functions
  526. * @param depth: The maximum depth of the trace
  527. */
  528. protected function traceInternal(target:Object, object:*, color:uint = 0x111111, functions:Boolean = false, depth:int = 4):void
  529. {
  530. if (isEnabled)
  531. {
  532. // Get the object information
  533. var xml:XML = XML(parseObject(object, "", functions, 1, depth));
  534. // Get memory
  535. var memory:uint = System.totalMemory;
  536. //Create a send object
  537. var obj:Object = {text:COMMAND_TRACE, date:new Date(), target:String(target), xml:xml, color:color, memory:memory};
  538. if (isConnected) {
  539. send(obj);
  540. } else {
  541. sendToBuffer(obj);
  542. }
  543. }
  544. }
  545. /**
  546. * Static clear traces function
  547. * This clears the traces in the application
  548. */
  549. public static function clearTraces():void
  550. {
  551. if (instance == null) instance = new MonsterDebugger(null);
  552. if (MonsterDebugger.enabled) instance.clearTracesInternal();
  553. }
  554. /**
  555. * Private clear traces function
  556. * This clears the traces in the application
  557. */
  558. protected function clearTracesInternal():void
  559. {
  560. //Create a send object
  561. if (isEnabled) {
  562. var obj:Object = {text:COMMAND_CLEAR_TRACES};
  563. if (isConnected) {
  564. send(obj);
  565. } else {
  566. sendToBuffer(obj);
  567. }
  568. }
  569. }
  570. /**
  571. * Check if an object is drawable displayobject
  572. * @param object: The object to check
  573. */
  574. protected function isDisplayObject(object:*):Boolean
  575. {
  576. return (object is DisplayObject || object is DisplayObjectContainer);
  577. }
  578. /**
  579. * Return an object
  580. * @param target: A point seperated path to the object
  581. * @param parent: Number of parents
  582. */
  583. protected function getObject(target:String = "", parent:int = 0):*
  584. {
  585. // Object to return
  586. var object:* = instance.root;
  587. // Check if the path is not empty
  588. if (target != "")
  589. {
  590. // Split the path
  591. var splitted:Array = target.split(".");
  592. // Loop through the array
  593. for (var i:int = 0; i < splitted.length - parent; i++)
  594. {
  595. // Check if the string isn't empty
  596. if (splitted[i] != "")
  597. {
  598. try
  599. {
  600. // Check if we should call the XML children function()
  601. // Or the getChildAt function
  602. // If not: Just update the path to the object
  603. if (splitted[i] == "children()") {
  604. object = object.children();
  605. } else if (splitted[i].indexOf("getChildAt(") == 0) {
  606. var index:Number = splitted[i].substring(11, splitted[i].indexOf(")", 11));
  607. object = DisplayObjectContainer(object).getChildAt(index);
  608. } else {
  609. object = object[splitted[i]];
  610. }
  611. }
  612. catch (error:ReferenceError)
  613. {
  614. // The object is not found
  615. var obj:Object = {text:COMMAND_NOTFOUND, target:target};
  616. if (isConnected) {
  617. send(obj);
  618. } else {
  619. sendToBuffer(obj);
  620. }
  621. break;
  622. }
  623. }
  624. }
  625. }
  626. // Return the object
  627. return object;
  628. }
  629. /**
  630. * Get the functions of an object
  631. * @param object: The object to parse
  632. * @param target: A point seperated path to the object
  633. */
  634. protected function getFunctions(object:*, target:String = ""):String
  635. {
  636. // The return string
  637. var xml:String = "";
  638. // Create the opening node
  639. xml += createNode("root");
  640. try
  641. {
  642. // Get the descriptor
  643. var description:XML = describeType(object);
  644. var type:String = parseType(description.@name);
  645. var childType:String = "";
  646. var childName:String = "";
  647. var childTarget:String = "";
  648. var methods:XMLList = description..method;
  649. var methodsArr:Array = new Array();
  650. var returnType:String;
  651. var parameters:XMLList;
  652. var args:Array;
  653. var argsString:String;
  654. var optional:Boolean = false;
  655. var double:Boolean = false;
  656. var i:int = 0;
  657. var n:int = 0;
  658. // Create the head node
  659. xml += createNode("node", {icon:ICON_DEFAULT, label:"(" + type + ")", target:target});
  660. // Save the methods
  661. // Filter out doubles (this should not happen though)
  662. for (i = 0; i < methods.length(); i++) {
  663. for (n = 0; n < methodsArr.length; n++) {
  664. if (methodsArr[n].name == methods[i].@name) {
  665. double = true;
  666. break;
  667. }
  668. }
  669. if (!double) {
  670. methodsArr.push({name:methods[i].@name, xml:methods[i], access:ACCESS_METHOD});
  671. }
  672. }
  673. // Sort the nodes
  674. methodsArr.sortOn("name");
  675. // Loop through the methods
  676. for (i = 0; i < methodsArr.length; i++)
  677. {
  678. // Save the type
  679. childType = TYPE_FUNCTION;
  680. childName = methodsArr[i].xml.@name;
  681. childTarget = target + "." + childName;
  682. // Save the function info
  683. // Parameters, arguments, return type, etc
  684. returnType = parseType(methodsArr[i].xml.@returnType);
  685. parameters = methodsArr[i].xml..parameter;
  686. args = new Array();
  687. argsString = "";
  688. optional = false;
  689. // Create the parameters
  690. for (n = 0; n < parameters.length(); n++)
  691. {
  692. // Optional parameters should start with a bracket
  693. if (parameters[n].@optional == "true" && !optional){
  694. optional = true;
  695. args.push("[");
  696. }
  697. // Push the parameter
  698. args.push(parseType(parameters[n].@type));
  699. }
  700. // The optional bracket is needed
  701. if (optional) {
  702. args.push("]");
  703. }
  704. // Create the arguments string
  705. argsString = args.join(", ");
  706. argsString = argsString.replace("[, ", "[");
  707. argsString = argsString.replace(", ]", "]");
  708. // Create the node
  709. xml += createNode("node", {
  710. icon: ICON_FUNCTION,
  711. label: childName + "(" + argsString + "):" + returnType,
  712. args: argsString,
  713. name: childName,
  714. type: TYPE_FUNCTION,
  715. access: ACCESS_METHOD,
  716. returnType: returnType,
  717. target: childTarget
  718. });
  719. // Loop through the parameters
  720. for (n = 0; n < parameters.length(); n++)
  721. {
  722. // Create the node
  723. xml += createNode("parameter", {
  724. type: parseType(parameters[n].@type),
  725. index: parameters[n].@index,
  726. optional: parameters[n].@optional
  727. }, true);
  728. }
  729. // Close the function node
  730. xml += createNode("/node");
  731. }
  732. // Create the head node
  733. xml += createNode("/node");
  734. }
  735. catch (error:Error)
  736. {
  737. // The object is not found
  738. var msg:String = "";
  739. msg += createNode("root");
  740. msg += createNode("node", {icon:ICON_WARNING, type:TYPE_WARNING, label:"Not found", name:"Not found"}, true);
  741. msg += createNode("/root");
  742. var obj:Object = {text:COMMAND_NOTFOUND, target:target, xml:XML(msg)};
  743. if (isConnected) {
  744. send(obj);
  745. } else {
  746. sendToBuffer(obj);
  747. }
  748. }
  749. // Create a closing node
  750. xml += createNode("/root");
  751. // Return the xml
  752. return xml;
  753. }
  754. /**
  755. * Parse an object
  756. * @param object: The object to parse
  757. * @param target: A point seperated path to the object
  758. * @param functions: Include or exclude functions
  759. * @param currentDepth: The current trace depth
  760. * @param maxDepth:: The maximum trace depth
  761. */
  762. protected function parseObject(object:*, target:String = "", functions:Boolean = false, currentDepth:int = 1, maxDepth:int = 4):String
  763. {
  764. // Variables needed in the loops
  765. var xml:String = "";
  766. var childType:String = "";
  767. var childName:String = "";
  768. var childTarget:String = "";
  769. var description:XML = new XML();
  770. var type:String = "";
  771. var base:String = "";
  772. var isXML:Boolean = false;
  773. var isXMLString:XML;
  774. var i:int = 0;
  775. var n:int = 0;
  776. // Check if the max trace depth is reached
  777. if (maxDepth == -1 || currentDepth <= maxDepth)
  778. {
  779. // Create the opening node if needed
  780. if (currentDepth == 1) xml += createNode("root");
  781. try
  782. {
  783. // Get the descriptor
  784. description = describeType(object);
  785. type = parseType(description.@name);
  786. base = parseType(description.@base);
  787. if (functions && base == TYPE_FUNCTION)
  788. {
  789. // Trace an empty function
  790. xml += createNode("node", {
  791. icon: ICON_FUNCTION,
  792. label: "(Function)",
  793. name: "",
  794. type: TYPE_FUNCTION,
  795. value: "",
  796. target: target,
  797. access: ACCESS_VARIABLE,
  798. permission: PERMISSION_READWRITE
  799. }, true);
  800. }
  801. else if (type == TYPE_ARRAY || type == TYPE_VECTOR)
  802. {
  803. // Add data description if needed
  804. if (currentDepth == 1) xml += createNode("node", {icon:ICON_ROOT, label:"(" + type + ")", target:target});
  805. // Create the length property
  806. // The only property of the array
  807. xml += createNode("node", {
  808. icon: ICON_VARIABLE,
  809. label: "length" + " (" + TYPE_UINT + ") = " + object["length"],
  810. name: "length",
  811. type: TYPE_UINT,
  812. value: object["length"],
  813. target: target + "." + "length",
  814. access: ACCESS_VARIABLE,
  815. permission: PERMISSION_READONLY
  816. }, true);
  817. // Get and sort the properties
  818. var keys:Array = new Array();
  819. for (var key:* in object) {
  820. keys.push(key);
  821. }
  822. keys.sort();
  823. // Loop through the array
  824. for (i = 0; i < keys.length; i++)
  825. {
  826. // Save the type
  827. childType = parseType(describeType(object[keys[i]]).@name);
  828. childTarget = target + "." + String(keys[i]);
  829. // Check if we can create a single string or a new node
  830. if (childType == TYPE_STRING || childType == TYPE_BOOLEAN || childType == TYPE_NUMBER || childType == TYPE_INT || childType == TYPE_UINT || childType == TYPE_FUNCTION)
  831. {
  832. isXML = false;
  833. isXMLString = new XML();
  834. // Check if the string is a XML string
  835. if (childType == TYPE_STRING) {
  836. try {
  837. isXMLString = new XML(object[keys[i]]);
  838. if (!isXMLString.hasSimpleContent() && isXMLString.children().length() > 0) isXML = true;
  839. } catch(error:TypeError) {}
  840. }
  841. try {
  842. if (!isXML) {
  843. xml += createNode("node", {
  844. icon: ICON_VARIABLE,
  845. label: "[" + keys[i] + "] (" + childType + ") = " + printObject(object[keys[i]], childType),
  846. name: "[" + keys[i] + "]",
  847. type: childType,
  848. value: printObject(object[keys[i]], childType),
  849. target: childTarget,
  850. access: ACCESS_VARIABLE,
  851. permission: PERMISSION_READWRITE
  852. }, true);
  853. } else {
  854. xml += createNode("node", {
  855. icon: ICON_VARIABLE,
  856. label: "[" + keys[i] + "] (" + childType + ")",
  857. name: "[" + keys[i] + "]",
  858. type: childType,
  859. value: "",
  860. target: childTarget,
  861. access: ACCESS_VARIABLE,
  862. permission: PERMISSION_READWRITE
  863. }, false);
  864. xml += parseXML(isXMLString, childTarget + "." + "cildren()", currentDepth, maxDepth);
  865. xml += createNode("/node");
  866. }
  867. } catch(error:Error) {}
  868. }
  869. else
  870. {
  871. xml += createNode("node", {
  872. icon: ICON_VARIABLE,
  873. label: "[" + keys[i] + "] (" + childType + ")",
  874. name: "[" + keys[i] + "]",
  875. type: childType,
  876. value: "",
  877. target: childTarget,
  878. access: ACCESS_VARIABLE,
  879. permission: PERMISSION_READWRITE
  880. });
  881. try
  882. {
  883. // Try to parse the object
  884. xml += parseObject(object[keys[i]], childTarget, functions, currentDepth + 1, maxDepth);
  885. }
  886. catch(error:Error)
  887. {
  888. // If this fails add a warning message for the user
  889. xml += createNode("node", {icon:ICON_WARNING, type:TYPE_WARNING, label:"Unreadable", name:"Unreadable"}, true);
  890. }
  891. xml += createNode("/node");
  892. }
  893. }
  894. // Close data description if needed
  895. if (currentDepth == 1) xml += createNode("/node");
  896. }
  897. else if (type == TYPE_OBJECT)
  898. {
  899. // Add data description if needed
  900. if (currentDepth == 1) xml += createNode("node", {icon:ICON_ROOT, label:"(" + type + ")", target:target});
  901. // Get and sort the properties
  902. var properties:Array = new Array();
  903. for (var prop:* in object) {
  904. properties.push(prop);
  905. }
  906. properties.sort();
  907. // Loop through the array
  908. for (i = 0; i < properties.length; i++)
  909. {
  910. // Save the type
  911. childType = parseType(describeType(object[properties[i]]).@name);
  912. childTarget = target + "." + properties[i];
  913. // Check if we can create a single string or a new node
  914. if (childType == TYPE_STRING || childType == TYPE_BOOLEAN || childType == TYPE_NUMBER || childType == TYPE_INT || childType == TYPE_UINT || childType == TYPE_FUNCTION)
  915. {
  916. isXML = false;
  917. isXMLString = new XML();
  918. // Check if the string is a XML string
  919. if (childType == TYPE_STRING) {
  920. try {
  921. isXMLString = new XML(object[properties[i]]);
  922. if (!isXMLString.hasSimpleContent() && isXMLString.children().length() > 0) isXML = true;
  923. } catch(error:TypeError) {}
  924. }
  925. try {
  926. if (!isXML) {
  927. xml += createNode("node", {
  928. icon: ICON_VARIABLE,
  929. label: properties[i] + " (" + childType + ") = " + printObject(object[properties[i]], childType),
  930. name: properties[i],
  931. type: childType,
  932. value: printObject(object[properties[i]], childType),
  933. target: childTarget,
  934. access: ACCESS_VARIABLE,
  935. permission: PERMISSION_READWRITE
  936. }, true);
  937. } else {
  938. xml += createNode("node", {
  939. icon: ICON_VARIABLE,
  940. label: properties[i] + " (" + childType + ")",
  941. name: properties[i],
  942. type: childType,
  943. value: "",
  944. target: childTarget,
  945. access: ACCESS_VARIABLE,
  946. permission: PERMISSION_READWRITE
  947. }, false);
  948. xml += parseXML(isXMLString, childTarget + "." + "cildren()", currentDepth, maxDepth);
  949. xml += createNode("/node");
  950. }
  951. } catch(error:Error) {}
  952. }
  953. else
  954. {
  955. xml += createNode("node", {
  956. icon: ICON_VARIABLE,
  957. label: properties[i] + " (" + childType + ")",
  958. name: properties[i],
  959. type: childType,
  960. value: "",
  961. target: childTarget,
  962. access: ACCESS_VARIABLE,
  963. permission: PERMISSION_READWRITE
  964. });
  965. try
  966. {
  967. // Try to parse the object
  968. xml += parseObject(object[properties[i]], childTarget, functions, currentDepth + 1, maxDepth);
  969. }
  970. catch(error:Error)
  971. {
  972. // If this fails add a warning message for the user
  973. xml += createNode("node", {icon:ICON_WARNING, type:TYPE_WARNING, label:"Unreadable", name:"Unreadable"}, true);
  974. }
  975. xml += createNode("/node");
  976. }
  977. }
  978. // Close data description if needed
  979. if (currentDepth == 1) xml += createNode("/node");
  980. }
  981. else if (type == TYPE_XML)
  982. {
  983. // Add data description if needed
  984. if (currentDepth == 1) xml += createNode("node", {icon:ICON_ROOT, label:"(" + type + ")", target:target});
  985. // Parse the XML
  986. xml += parseXML(object, target + "." + "cildren()", currentDepth, maxDepth);
  987. // Close data description if needed
  988. if (currentDepth == 1) xml += createNode("/node");
  989. }
  990. else if (type == TYPE_XMLLIST)
  991. {
  992. // Add data description if needed
  993. if (currentDepth == 1) xml += createNode("node", {icon:ICON_ROOT, label:"(" + type + ")", target:target});
  994. // Create the length property
  995. // The only property of the array
  996. xml += createNode("node", {
  997. icon: ICON_VARIABLE,
  998. label: "length" + " (" + TYPE_UINT + ") = " + object.length(),
  999. name: "length",
  1000. type: TYPE_UINT,
  1001. value: object.length(),
  1002. target: target + "." + "length",
  1003. access: ACCESS_VARIABLE,
  1004. permission: PERMISSION_READONLY
  1005. }, true);
  1006. // Loop through the xml nodes
  1007. for (i = 0; i < object.length(); i++)
  1008. {
  1009. xml += parseXML(object[i], target + "." + String(i) + ".children()", currentDepth, maxDepth);
  1010. }
  1011. // Close data description if needed
  1012. if (currentDepth == 1) xml += createNode("/node");
  1013. }
  1014. else if (type == TYPE_STRING || type == TYPE_BOOLEAN || type == TYPE_NUMBER || type == TYPE_INT || type == TYPE_UINT)
  1015. {
  1016. isXML = false;
  1017. isXMLString = new XML();
  1018. // Check if the string is a XML string
  1019. if (type == TYPE_STRING) {
  1020. try {
  1021. isXMLString = new XML(object);
  1022. if (!isXMLString.hasSimpleContent() && isXMLString.children().length() > 0) isXML = true;
  1023. } catch(error:TypeError) {}
  1024. }
  1025. try {
  1026. if (!isXML) {
  1027. xml += createNode("node", {
  1028. icon: ICON_VARIABLE,
  1029. label: "(" + type + ") = " + printObject(object, type),
  1030. name: "",
  1031. type: type,
  1032. value: printObject(object, type),
  1033. target: target,
  1034. access: ACCESS_VARIABLE,
  1035. permission: PERMISSION_READWRITE
  1036. }, true);
  1037. } else {
  1038. xml += createNode("node", {
  1039. icon: ICON_VARIABLE,
  1040. label: "(" + type + ")",
  1041. name: "",
  1042. type: type,
  1043. value: "",
  1044. target: target,
  1045. access: ACCESS_VARIABLE,
  1046. permission: PERMISSION_READWRITE
  1047. }, false);
  1048. xml += parseXML(isXMLString, target + "." + "cildren()", currentDepth, maxDepth);
  1049. xml += createNode("/node");
  1050. }
  1051. } catch(error:Error) {}
  1052. }
  1053. else
  1054. {
  1055. // Add data description if needed
  1056. if (currentDepth == 1) xml += createNode("node", {icon:ICON_ROOT, label:"(" + type + ")", target:target});
  1057. // Get the data
  1058. var variables:XMLList = description..variable;
  1059. var accessors:XMLList = description..accessor;
  1060. var constants:XMLList = description..constant;
  1061. var methods:XMLList = description..method;
  1062. var variablesArr:Array = new Array();
  1063. var methodsArr:Array = new Array();
  1064. var double:Boolean = false;
  1065. var permission:String = "";
  1066. var icon:String = "";
  1067. // Save the variables
  1068. double = false;
  1069. for (i = 0; i < variables.length(); i++) {
  1070. for (n = 0; n < variablesArr.length; n++) {
  1071. if (variablesArr[n].name == variables[i].@name) {
  1072. double = true;
  1073. break;
  1074. }
  1075. }
  1076. if (!double) {
  1077. variablesArr.push({name:variables[i].@name, xml:variables[i], access:ACCESS_VARIABLE});
  1078. }
  1079. }
  1080. // Save the accessors
  1081. double = false;
  1082. for (i = 0; i < accessors.length(); i++) {
  1083. for (n = 0; n < variablesArr.length; n++) {
  1084. if (variablesArr[n].name == accessors[i].@name) {
  1085. double = true;
  1086. break;
  1087. }
  1088. }
  1089. if (!double) {
  1090. variablesArr.push({name:accessors[i].@name, xml:accessors[i], access:ACCESS_ACCESSOR});
  1091. }
  1092. }
  1093. // Save the constants
  1094. double = false;
  1095. for (i = 0; i < constants.length(); i++) {
  1096. for (n = 0; n < variablesArr.length; n++) {
  1097. if (variablesArr[n].name == constants[i].@name) {
  1098. double = true;
  1099. break;
  1100. }
  1101. }
  1102. if (!double) {
  1103. variablesArr.push({name:constants[i].@name, xml:constants[i], access:ACCESS_CONSTANT});
  1104. }
  1105. }
  1106. // Save the methods
  1107. double = false;
  1108. for (i = 0; i < methods.length(); i++) {
  1109. for (n = 0; n < methodsArr.length; n++) {
  1110. if (methodsArr[n].name == methods[i].@name) {
  1111. double = true;
  1112. break;
  1113. }
  1114. }
  1115. if (!double) {
  1116. methodsArr.push({name:methods[i].@name, xml:methods[i], access:ACCESS_METHOD});
  1117. }
  1118. }
  1119. // Sort the nodes
  1120. variablesArr.sortOn("name");
  1121. methodsArr.sortOn("name");
  1122. // VARIABLES
  1123. for (i = 0; i < variablesArr.length; i++)
  1124. {
  1125. // Save the type
  1126. childType = parseType(variablesArr[i].xml.@type);
  1127. childName = variablesArr[i].xml.@name;
  1128. childTarget = target + "." + childName;
  1129. // Save the permission and icon
  1130. permission = PERMISSION_READWRITE;
  1131. icon = ICON_VARIABLE;
  1132. // Check for read / write permissions
  1133. if (variablesArr[i].access == ACCESS_CONSTANT) {
  1134. // Constant
  1135. permission = PERMISSION_READONLY;
  1136. icon = ICON_VARIABLE_READONLY;
  1137. }
  1138. if (variablesArr[i].xml.@access == PERMISSION_READONLY) {
  1139. // Only a getter
  1140. permission = PERMISSION_READONLY;
  1141. icon = ICON_VARIABLE_READONLY;
  1142. }
  1143. if (variablesArr[i].xml.@access == PERMISSION_WRITEONLY) {
  1144. // Only a setter
  1145. permission = PERMISSION_WRITEONLY;
  1146. icon = ICON_VARIABLE_WRITEONLY;
  1147. }
  1148. // Don't include write only accessor
  1149. if (permission != PERMISSION_WRITEONLY)
  1150. {
  1151. // Check if we can create a single string or a new node
  1152. if (childType == TYPE_STRING || childType == TYPE_BOOLEAN || childType == TYPE_NUMBER || childType == TYPE_INT || childType == TYPE_UINT || childType == TYPE_FUNCTION)
  1153. {
  1154. isXML = false;
  1155. isXMLString = new XML();
  1156. // Check if the string is a XML string
  1157. if (childType == TYPE_STRING) {
  1158. try {
  1159. isXMLString = new XML(object[childName]);
  1160. if (!isXMLString.hasSimpleContent() && isXMLString.children().length() > 0) isXML = true;
  1161. } catch(error:TypeError) {}
  1162. }
  1163. try {
  1164. if (!isXML) {
  1165. xml += createNode("node", {
  1166. icon: icon,
  1167. label: childName + " (" + childType + ") = " + printObject(object[childName], childType),
  1168. name: childName,
  1169. type: childType,
  1170. value: printObject(object[childName], childType),
  1171. target: childTarget,
  1172. access: variablesArr[i].access,
  1173. permission: permission
  1174. }, true);
  1175. } else {
  1176. xml += createNode("node", {
  1177. icon: icon,
  1178. label: childName + " (" + childType + ")",
  1179. name: childName,
  1180. type: childType,
  1181. value: "",
  1182. target: childTarget,
  1183. access: variablesArr[i].access,
  1184. permission: permission
  1185. }, false);
  1186. xml += parseXML(isXMLString, childTarget + "." + "cildren()", currentDepth, maxDepth);
  1187. xml += createNode("/node");
  1188. }
  1189. } catch(error:Error) {}
  1190. }
  1191. else
  1192. {
  1193. xml += createNode("node", {
  1194. icon: icon,
  1195. label: childName + " (" + childType + ")",
  1196. name: childName,
  1197. type: childType,
  1198. target: childTarget,
  1199. access: variablesArr[i].access,
  1200. permission: permission
  1201. });
  1202. try
  1203. {
  1204. // Try to parse the object
  1205. xml += parseObject(object[childName], childTarget, functions, currentDepth + 1, maxDepth);
  1206. }
  1207. catch(error:Error)
  1208. {
  1209. // If this fails add a warning message for the user
  1210. xml += createNode("node", {icon:ICON_WARNING, type:TYPE_WARNING, label:"Unreadable", name:"Unreadable"}, true);
  1211. }
  1212. xml += createNode("/node");
  1213. }
  1214. }
  1215. }
  1216. // METHODS
  1217. if (functions)
  1218. {
  1219. for (i = 0; i < methodsArr.length; i++)
  1220. {
  1221. // Save the type
  1222. childType = TYPE_FUNCTION;
  1223. childName = methodsArr[i].xml.@name;
  1224. childTarget = target + "." + childName;
  1225. // Save the parameters
  1226. var returnType:String = parseType(methodsArr[i].xml.@returnType);
  1227. var parameters:XMLList = methodsArr[i].xml..parameter;
  1228. var args:Array = new Array();
  1229. // Create the parameters
  1230. for (n = 0; n < parameters.length(); n++) {
  1231. args.push(parseType(parameters[n].@type));
  1232. }
  1233. // Create the node
  1234. xml += createNode("node", {
  1235. icon: ICON_FUNCTION,
  1236. label: childName + "(" + args.join(", ") + "):" + returnType,
  1237. args: args.join(", "),
  1238. name: childName,
  1239. type: TYPE_FUNCTION,
  1240. access: variablesArr[i].access,
  1241. returnType: returnType,
  1242. target: childTarget
  1243. }, true);
  1244. }
  1245. }
  1246. // Close data description if needed
  1247. if (currentDepth == 1) xml += createNode("/node");
  1248. }
  1249. }
  1250. catch (error:Error)
  1251. {
  1252. // The object is not found
  1253. var msg:String = "";
  1254. msg += createNode("root");
  1255. msg += createNode("node", {icon:ICON_WARNING, type:TYPE_WARNING, label:"Not found", name:"Not found"}, true);
  1256. msg += createNode("/root");
  1257. var obj:Object = {text:COMMAND_NOTFOUND, target:target, xml:XML(msg)};
  1258. if (isConnected) {
  1259. send(obj);
  1260. } else {
  1261. sendToBuffer(obj);
  1262. }
  1263. }
  1264. // Create a closing node if needed
  1265. if (currentDepth == 1) xml += createNode("/root");
  1266. }
  1267. //Return the xml
  1268. return xml;
  1269. }
  1270. /**
  1271. * Parse a display object
  1272. * @param object: The object to parse
  1273. * @param target: A point seperated path to the object
  1274. * @param functions: Include or exclude functions
  1275. * @param currentDepth: The current trace depth
  1276. * @param maxDepth:: The maximum trace depth
  1277. */
  1278. protected function parseDisplayObject(object:*, target:String = "", functions:Boolean = false, currentDepth:int = 1, maxDepth:int = 4):String
  1279. {
  1280. // Variables needed in the loops
  1281. var xml:String = "";
  1282. var childs:Array;
  1283. var child:DisplayObject;
  1284. var childType:String = "";
  1285. var childIcon:String = "";
  1286. var childName:String = "";
  1287. var childTarget:String = "";
  1288. var childChildren:String = "";
  1289. var i:int = 0;
  1290. // Check if the max trace depth is reached
  1291. if (maxDepth == -1 || currentDepth <= maxDepth)
  1292. {
  1293. // Create the opening node if needed
  1294. if (currentDepth == 1) xml += createNode("root");
  1295. try
  1296. {
  1297. // Add data description if needed
  1298. if (currentDepth == 1) {
  1299. var ojectName:String = DisplayObject(object).name;
  1300. if (ojectName == null || ojectName == "null") {
  1301. ojectName = "DisplayObject";
  1302. }
  1303. xml += createNode("node", {icon:ICON_ROOT, label:"(" + ojectName + ")", target:target});
  1304. }
  1305. // Get the childs
  1306. childs = new Array();
  1307. for (i = 0; i < DisplayObjectContainer(object).numChildren; i++) {
  1308. childs.push(DisplayObjectContainer(object).getChildAt(i));
  1309. }
  1310. // Loop through the array
  1311. for (i = 0; i < childs.length; i++)
  1312. {
  1313. // Save the child properties
  1314. child = childs[i];
  1315. childName = describeType(child).@name;
  1316. childType = parseType(childName);
  1317. childTarget = target + "." + "getChildAt(" + i + ")";
  1318. childIcon = child is DisplayObjectContainer ? ICON_ROOT : ICON_VARIABLE;
  1319. childChildren = child is DisplayObjectContainer ? String(DisplayObjectContainer(child).numChildren) : "";
  1320. // Create the node
  1321. xml += createNode("node", {
  1322. icon: childIcon,
  1323. label: child.name + " (" + childType + ") " + childChildren,
  1324. name: child.name,
  1325. type: childType,
  1326. value: printObject(child, childType),
  1327. target: childTarget,
  1328. access: ACCESS_VARIABLE,
  1329. permission: PERMISSION_READWRITE
  1330. });
  1331. try
  1332. {
  1333. // Try to parse the object
  1334. xml += parseDisplayObject(child, childTarget, functions, currentDepth + 1, maxDepth);
  1335. }
  1336. catch(error:Error)
  1337. {
  1338. // If this fails add a warning message for the user
  1339. xml += createNode("node", {icon:ICON_WARNING, type:TYPE_WARNING, label:"Unreadable", name:"Unreadable"}, true);
  1340. }
  1341. xml += createNode("/node");
  1342. }
  1343. // Create a closing node if needed
  1344. if (currentDepth == 1) xml += createNode("/node");
  1345. }
  1346. catch (error:Error)
  1347. {
  1348. // The object is not found
  1349. var msg:String = "";
  1350. msg += createNode("root");
  1351. msg += createNode("node", {icon:ICON_WARNING, type:TYPE_WARNING, label:"Not found", name:"Not found"}, true);
  1352. msg += createNode("/root");
  1353. var obj:Object = {text:COMMAND_NOTFOUND, target:target, xml:XML(msg)};
  1354. if (isConnected) {
  1355. send(obj);
  1356. } else {
  1357. sendToBuffer(obj);
  1358. }
  1359. }
  1360. // Create a closing node if needed
  1361. if (currentDepth == 1) xml += createNode("/root");
  1362. }
  1363. //Return the xml
  1364. return xml;
  1365. }
  1366. /**
  1367. * Parse a XML node
  1368. * @param node: The xml to parse
  1369. * @param target: A point seperated path to the object
  1370. * @param currentDepth: The current trace depth
  1371. * @param maxDepth:: The maximum trace depth
  1372. */
  1373. protected function parseXML(node:*, target:String = "", currentDepth:int = 1, maxDepth:int = -1):String
  1374. {
  1375. // Create a return string
  1376. var xml:String = "";
  1377. var i:int = 0;
  1378. // Check if the maximum trace depth is reached
  1379. if (maxDepth == -1 || currentDepth <= maxDepth)
  1380. {
  1381. // Check if the user traced an attribute
  1382. if (target.indexOf("@") != -1)
  1383. {
  1384. // Display a single attribute
  1385. xml += createNode("node", {
  1386. icon: ICON_XMLATTRIBUTE,
  1387. label: node,
  1388. name: "",
  1389. type: TYPE_XMLATTRIBUTE,
  1390. value: node,
  1391. target: target,
  1392. access: ACCESS_VARIABLE,
  1393. permission: PERMISSION_READWRITE
  1394. }, true);
  1395. }
  1396. else if (node.name() == null)
  1397. {
  1398. // Only a text value
  1399. xml += createNode("node", {
  1400. icon: ICON_XMLVALUE,
  1401. label: "(" + TYPE_XMLVALUE + ") = " + printObject(node, TYPE_XMLVALUE),
  1402. name: "",
  1403. type: TYPE_XMLVALUE,
  1404. value: printObject(node, TYPE_XMLVALUE),
  1405. target: target,
  1406. access: ACCESS_VARIABLE,
  1407. permission: PERMISSION_READWRITE
  1408. }, true);
  1409. }
  1410. else if (node.hasSimpleContent())
  1411. {
  1412. // Node with one text value and possible attributes
  1413. xml += createNode("node", {
  1414. icon: ICON_XMLNODE,
  1415. label: node.name() + " (" + TYPE_XMLNODE + ")",
  1416. name: node.name(),
  1417. type: TYPE_XMLNODE,
  1418. value: "",
  1419. target: target,
  1420. access: ACCESS_VARIABLE,
  1421. permission: PERMISSION_READWRITE
  1422. });
  1423. // Only a text value
  1424. if (node != "") {
  1425. xml += createNode("node", {
  1426. icon: ICON_XMLVALUE,
  1427. label: "(" + TYPE_XMLVALUE + ") = " + printObject(node, TYPE_XMLVALUE),
  1428. name: "",
  1429. type: TYPE_XMLVALUE,
  1430. value: printObject(node, TYPE_XMLVALUE),
  1431. target: target,
  1432. access: ACCESS_VARIABLE,
  1433. permission: PERMISSION_READWRITE
  1434. }, true);
  1435. }
  1436. // Loop through the arrributes
  1437. for (i = 0; i < node.attributes().length(); i++)
  1438. {
  1439. xml += createNode("node", {
  1440. icon: ICON_XMLATTRIBUTE,
  1441. label: "@" + node.attributes()[i].name() + " (" + TYPE_XMLATTRIBUTE + ") = " + node.attributes()[i],
  1442. name: "",
  1443. type: TYPE_XMLATTRIBUTE,
  1444. value: node.attributes()[i],
  1445. target: target + "." + "@" + node.attributes()[i].name(),
  1446. access: ACCESS_VARIABLE,
  1447. permission: PERMISSION_READWRITE
  1448. }, true);
  1449. }
  1450. // Close the node
  1451. xml += createNode("/node");
  1452. }
  1453. else
  1454. {
  1455. // Node with children and attributes
  1456. // This node has no value due to the children
  1457. xml += createNode("node", {
  1458. icon: ICON_XMLNODE,
  1459. label: node.name() + " (" + TYPE_XMLNODE + ")",
  1460. name: node.name(),
  1461. type: TYPE_XMLNODE,
  1462. value: "",
  1463. target: target,
  1464. access: ACCESS_VARIABLE,
  1465. permission: PERMISSION_READWRITE
  1466. });
  1467. // Loop through the arrributes
  1468. for (i = 0; i < node.attributes().length(); i++)
  1469. {
  1470. xml += createNode("node", {
  1471. icon: ICON_XMLATTRIBUTE,
  1472. label: "@" + node.attributes()[i].name() + " (" + TYPE_XMLATTRIBUTE + ") = " + node.attributes()[i],
  1473. name: "",
  1474. type: TYPE_XMLATTRIBUTE,
  1475. value: node.attributes()[i],
  1476. target: target + "." + "@" + node.attributes()[i].name(),
  1477. access: ACCESS_VARIABLE,
  1478. permission: PERMISSION_READWRITE
  1479. }, true);
  1480. }
  1481. // Loop through children
  1482. for (i = 0; i < node.children().length(); i++)
  1483. {
  1484. var childTarget:String = target + "." + "children()" + "." + i;
  1485. xml += parseXML(node.children()[i], childTarget, currentDepth + 1, maxDepth);
  1486. }
  1487. // Close the node
  1488. xml += createNode("/node");
  1489. }
  1490. }
  1491. // Return the formatted XML
  1492. return xml;
  1493. }
  1494. /**
  1495. * Converts package names to type
  1496. * Example: "nl.demonsters.debugger::MonsterDebugger" becomes "MonsterDebugger"
  1497. * We could also use getDefinitionByName() but that can't parse "builtin.as$0::MethodClosure"
  1498. * @param type: The string to parse
  1499. */
  1500. protected function parseType(type:String):String
  1501. {
  1502. // The return string
  1503. var s:String = type;
  1504. // Remove the package information if needed
  1505. // Magic number 2 = length of ::
  1506. if (type.lastIndexOf("::") != -1) {
  1507. s = type.substring(type.lastIndexOf("::") + 2, type.length);
  1508. }
  1509. // Remove the items after the .
  1510. // Vector.<String> becomes Vector
  1511. // Magic number 1 = length of .
  1512. if (s.lastIndexOf(".") != -1) {
  1513. s = s.substring(0, s.lastIndexOf("."));
  1514. }
  1515. // Change "MethodClosure" to "Function"
  1516. // This is better for the user interface
  1517. if (s == TYPE_METHOD) {
  1518. s = TYPE_FUNCTION;
  1519. }
  1520. // Return the value
  1521. return htmlEscape(s);
  1522. }
  1523. /**
  1524. * Create an XML node
  1525. * @param title: The title of the node
  1526. * @param object: The values of the node
  1527. * @param close: Create a closing tag
  1528. */
  1529. protected function createNode(title:String, object:Object = null, close:Boolean = false):String
  1530. {
  1531. // The string to store the node
  1532. var xml:String = "";
  1533. // Open the node
  1534. xml += "<" + title;
  1535. //Loop through the values
  1536. if (object) {
  1537. for (var prop:* in object) {
  1538. xml += " " + prop + "=\"" + object[prop] + "\"";
  1539. }
  1540. }
  1541. // Close the node
  1542. if (close) {
  1543. xml += "></" + title + ">";
  1544. } else {
  1545. xml += ">";
  1546. }
  1547. // Return the node
  1548. return xml;
  1549. }
  1550. /**
  1551. * Print a single object
  1552. * @param object: The object to parse
  1553. * @param type: The object type
  1554. */
  1555. protected function printObject(object:*, type:String):String
  1556. {
  1557. // Create a return string
  1558. var s:String = "";
  1559. // Check if the object can be converted
  1560. // Here we can handle the exceptions
  1561. if (type == TYPE_BYTEARRAY)
  1562. {
  1563. // We dont want to send the complete byte array
  1564. // Only display the number of bytes
  1565. s = object["length"] + " bytes";
  1566. }
  1567. else
  1568. {
  1569. // Display the object
  1570. s = htmlEscape(String(object));
  1571. }
  1572. // Return the printed object
  1573. return s;
  1574. }
  1575. /**
  1576. * Send the current FPS
  1577. * @param event: Basic event
  1578. */
  1579. protected function enterFrameHandler(event:Event):void
  1580. {
  1581. if (isEnabled) {
  1582. monitorFrames++;
  1583. }
  1584. }
  1585. /**
  1586. * Send the current memory consumption
  1587. * @param event: Basic timer event
  1588. */
  1589. protected function monitorHandler(event:TimerEvent):void
  1590. {
  1591. if (isEnabled)
  1592. {
  1593. // Get the total Flash Player memory consumption
  1594. // Note: This includes all Flash Player instances
  1595. var memory:uint = System.totalMemory;
  1596. // Calculate the frames pro second
  1597. var now:Number = new Date().time;
  1598. var delta:Number = now - monitorTime;
  1599. var fps:uint = monitorFrames / delta * 1000; // Miliseconds to seconds
  1600. monitorFrames = 0;
  1601. monitorTime = now;
  1602. // Send the data
  1603. var obj:Object = {text:COMMAND_MONITOR, memory:memory, fps:fps, time:now, start:monitorStart};
  1604. if (isConnected) {
  1605. send(obj);
  1606. } else {
  1607. sendToBuffer(obj);
  1608. }
  1609. }
  1610. }
  1611. /**
  1612. * Converts regular characters to HTML characters
  1613. * @param s: The string to convert
  1614. */
  1615. protected function htmlEscape(s:String):String
  1616. {
  1617. if (s) {
  1618. // Remove html elements
  1619. if (s.indexOf("&") != -1) {
  1620. s = s.split("&").join("&amp;");
  1621. }
  1622. if (s.indexOf("<") != -1) {
  1623. s = s.split("<").join("&lt;");
  1624. }
  1625. if (s.indexOf(">") != -1) {
  1626. s = s.split(">").join("&gt;");
  1627. }
  1628. if (s.indexOf("\'") != -1) {
  1629. s = s.split("\'").join("&apos;");
  1630. }
  1631. if (s.indexOf("\"") != -1) {
  1632. s = s.split("\"").join("&quot;");
  1633. }
  1634. // Return the xml
  1635. var xml:XML = <a>{s}</a>;
  1636. return xml.toXMLString().replace(/(^<a>)|(<\/a>$)|(^<a\/>$)/g, "");
  1637. } else {
  1638. return "";
  1639. }
  1640. }
  1641. /**
  1642. * Enable or disable the debugger
  1643. */
  1644. public static function get enabled():Boolean
  1645. {
  1646. if (instance == null) instance = new MonsterDebugger(null);
  1647. return instance.isEnabled;
  1648. }
  1649. public static function set enabled(value:Boolean):void
  1650. {
  1651. if (instance == null) instance = new MonsterDebugger(null);
  1652. instance.isEnabled = value;
  1653. }
  1654. /**
  1655. * Event handlers for localconnection
  1656. * Disconnect on error
  1657. */
  1658. private function asyncErrorHandler(event:AsyncErrorEvent):void {
  1659. isConnected = false;
  1660. }
  1661. private function securityErrorHandler(event:SecurityErrorEvent):void {
  1662. isConnected = false;
  1663. }
  1664. private function statusHandler(event:StatusEvent):void {
  1665. if (event.level == "error") {
  1666. isConnected = false;
  1667. }
  1668. }
  1669. }
  1670. }