1/* 2Copyright � 2006 Adobe Systems Incorporated 3 4Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), 5to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 6and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 7 8 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 9 10 11THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 12FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 13LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 14OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 16*/ 17 18 19/* 20 * The Bridge class, responsible for navigating JS instances 21 */ 22package bridge 23{ 24 25/* 26 * imports 27 */ 28import flash.external.ExternalInterface; 29import flash.utils.Timer; 30import flash.events.*; 31import flash.display.DisplayObject; 32import flash.system.ApplicationDomain; 33import flash.utils.Dictionary; 34import flash.utils.setTimeout; 35 36import mx.collections.errors.ItemPendingError; 37import mx.core.IMXMLObject; 38 39import flash.utils.getQualifiedClassName; 40import flash.utils.describeType; 41import flash.events.TimerEvent; 42 43/** 44 * The FABridge class, responsible for proxying AS objects into javascript 45 */ 46public class FABridge extends EventDispatcher implements IMXMLObject 47{ 48 49 //holds a list of stuff to call later, to break the recurrence of the js <> as calls 50 //you must use the full class name, as returned by the getQualifiedClassName() function 51 public static const MethodsToCallLater:Object = new Object(); 52 MethodsToCallLater["mx.collections::ArrayCollection"]="refresh,removeItemAt"; 53 54 public static const EventsToCallLater:Object = new Object(); 55 EventsToCallLater["mx.data.events::UnresolvedConflictsEvent"]="true"; 56 EventsToCallLater["mx.events::PropertyChangeEvent"]="true"; 57 58 public static const INITIALIZED:String = "bridgeInitialized"; 59 60 // constructor 61 public function FABridge() 62 { 63 super(); 64 initializeCallbacks(); 65 } 66 67 // private vars 68 69 /** 70 * stores a cache of descriptions of AS types suitable for sending to JS 71 */ 72 private var localTypeMap:Dictionary = new Dictionary(); 73 74 /** 75 * stores an id-referenced dictionary of objects exported to JS 76 */ 77 private var localInstanceMap:Dictionary = new Dictionary(); 78 79 /** 80 * stores an id-referenced dictionary of functions exported to JS 81 */ 82 private var localFunctionMap:Dictionary = new Dictionary(); 83 84 /** 85 * stores an id-referenced dictionary of proxy functions imported from JS 86 */ 87 private var remoteFunctionCache:Dictionary = new Dictionary(); 88 89 /** 90 * stores a list of custom serialization functions 91 */ 92 private var customSerializersMap:Dictionary = new Dictionary(); 93 94 /** 95 * stores a map of object ID's and their reference count 96 */ 97 private var refMap:Dictionary = new Dictionary(); 98 /** 99 * a local counter for generating unique IDs 100 */ 101 private var nextID:Number = 0; 102 103 private var lastRef:int; 104 105 /* values that can't be serialized natively across the bridge are packed and identified by type. 106 These constants represent different serialization types */ 107 public static const TYPE_ASINSTANCE:uint = 1; 108 public static const TYPE_ASFUNCTION:uint = 2; 109 public static const TYPE_JSFUNCTION:uint = 3; 110 public static const TYPE_ANONYMOUS:uint = 4; 111 112 private var _initChecked:Boolean = false; 113 114 // properties 115 116 //getters and setters for the main component in the swf - the root 117 public function get rootObject():DisplayObject {return _rootObject;} 118 public function set rootObject(value:DisplayObject):void 119 { 120 _rootObject = value; 121 checkInitialized(); 122 } 123 124 /** 125 * the bridge name 126 */ 127 public var bridgeName:String; 128 private var _registerComplete:Boolean = false; 129 130 /** 131 * increment the reference count for an object being passed over the bridge 132 */ 133 public function incRef(objId:int):void 134 { 135 if(refMap[objId] == null) { 136 //the object is being created; we now add it to the map and set its refCount = 1 137 refMap[objId] = 1; 138 } else { 139 refMap[objId] = refMap[objId] +1; 140 } 141 } 142 143 /** 144 * when an object has been completely passed to JS its reference count is decreased with 1 145 */ 146 public function releaseRef(objId:int):void 147 { 148 if(refMap[objId] != null) 149 { 150 var newRefVal:int = refMap[objId] - 1; 151 // if the object exists in the referenceMap and its count equals or has dropped under 0 we clean it up 152 if(refMap[objId] != null && newRefVal <= 0) 153 { 154 delete refMap[objId]; 155 delete localInstanceMap[objId]; 156 } 157 else 158 { 159 refMap[objId] = newRefVal; 160 } 161 } 162 } 163 164 /** 165 * attaches the callbacks to external interface 166 */ 167 public function initializeCallbacks():void 168 { 169 if (ExternalInterface.available == false) 170 { 171 return; 172 } 173 174 ExternalInterface.addCallback("getRoot", js_getRoot); 175 ExternalInterface.addCallback("getPropFromAS", js_getPropFromAS); 176 ExternalInterface.addCallback("setPropInAS", js_setPropertyInAS); 177 ExternalInterface.addCallback("invokeASMethod", js_invokeMethod); 178 ExternalInterface.addCallback("invokeASFunction", js_invokeFunction); 179 ExternalInterface.addCallback("releaseASObjects", js_releaseASObjects); 180 ExternalInterface.addCallback("create", js_create); 181 ExternalInterface.addCallback("releaseNamedASObject",js_releaseNamedASObject); 182 ExternalInterface.addCallback("incRef", incRef); 183 ExternalInterface.addCallback("releaseRef", releaseRef); 184 } 185 186 private var _rootObject:DisplayObject; 187 188 private var _document:DisplayObject; 189 190 /** 191 * called to check whether the bridge has been initialized for the specified document/id pairs 192 */ 193 public function initialized(document:Object, id:String):void 194 { 195 _document = (document as DisplayObject); 196 197 if (_document != null) 198 { 199 checkInitialized(); 200 } 201 } 202 203 private function get baseObject():DisplayObject 204 { 205 return (rootObject == null)? _document:rootObject; 206 } 207 208 209 private function checkInitialized():void 210 { 211 if(_initChecked== true) 212 { 213 return; 214 } 215 _initChecked = true; 216 217 // oops! timing error. Player team is working on it. 218 var t:Timer = new Timer(200,1); 219 t.addEventListener(TimerEvent.TIMER,auxCheckInitialized); 220 t.start(); 221 } 222 223 /** 224 * auxiliary initialization check that is called after the timing has occurred 225 */ 226 private function auxCheckInitialized(e:Event):void 227 { 228 229 var bCanGetParams:Boolean = true; 230 231 try 232 { 233 var params:Object = baseObject.root.loaderInfo.parameters; 234 } 235 catch (e:Error) 236 { 237 bCanGetParams = false; 238 } 239 240 if (bCanGetParams == false) 241 { 242 var t:Timer = new Timer(100); 243 var timerFunc:Function = function(e:TimerEvent):void 244 { 245 if(baseObject.root != null) 246 { 247 try 248 { 249 bCanGetParams = true; 250 var params:Object = baseObject.root.loaderInfo.parameters; 251 } 252 catch (err:Error) 253 { 254 bCanGetParams = false; 255 } 256 if (bCanGetParams) 257 { 258 t.removeEventListener(TimerEvent.TIMER, timerFunc); 259 t.stop(); 260 dispatchInit(); 261 } 262 } 263 } 264 t.addEventListener(TimerEvent.TIMER, timerFunc); 265 t.start(); 266 } 267 else 268 { 269 dispatchInit(); 270 } 271 } 272 273 /** 274 * call into JS to annunce that the bridge is ready to be used 275 */ 276 private function dispatchInit(e:Event = null):void 277 { 278 if(_registerComplete == true) 279 { 280 return; 281 } 282 283 if (ExternalInterface.available == false) 284 { 285 return; 286 } 287 288 if (bridgeName == null) 289 { 290 bridgeName = baseObject.root.loaderInfo.parameters["bridgeName"]; 291 292 if(bridgeName == null) 293 { 294 bridgeName = "flash"; 295 } 296 } 297 298 _registerComplete = ExternalInterface.call("FABridge__bridgeInitialized", [bridgeName]); 299 dispatchEvent(new Event(FABridge.INITIALIZED)); 300 } 301 302 // serialization/deserialization 303 304 /** serializes a value for transfer across the bridge. primitive types are left as is. Arrays are left as arrays, but individual 305 * values in the array are serialized according to their type. Functions and class instances are inserted into a hash table and sent 306 * across as keys into the table. 307 * 308 * For class instances, if the instance has been sent before, only its id is passed. If This is the first time the instance has been sent, 309 * a ref descriptor is sent associating the id with a type string. If this is the first time any instance of that type has been sent 310 * across, a descriptor indicating methods, properties, and variables of the type is also sent across 311 */ 312 public function serialize(value:*, keep_refs:Boolean=false):* 313 { 314 var result:* = {}; 315 result.newTypes = []; 316 result.newRefs = {}; 317 318 if (value is Number || value is Boolean || value is String || value == null || value == undefined || value is int || value is uint) 319 { 320 result = value; 321 } 322 else if (value is Array) 323 { 324 result = []; 325 for(var i:int = 0; i < value.length; i++) 326 { 327 result[i] = serialize(value[i], keep_refs); 328 } 329 } 330 else if (value is Function) 331 { 332 // serialize a class 333 result.type = TYPE_ASFUNCTION; 334 result.value = getFunctionID(value, true); 335 } 336 else if (getQualifiedClassName(value) == "Object") 337 { 338 result.type = TYPE_ANONYMOUS; 339 result.value = value; 340 } 341 else 342 { 343 // serialize a class 344 result.type = TYPE_ASINSTANCE; 345 // make sure the type info is available 346 var className:String = getQualifiedClassName(value); 347 348 var serializer:Function = customSerializersMap[className]; 349 350 // try looking up the serializer under an alternate name 351 if (serializer == null) 352 { 353 if (className.indexOf('$') > 0) 354 { 355 var split:int = className.lastIndexOf(':'); 356 if (split > 0) 357 { 358 var alternate:String = className.substring(split+1); 359 serializer = customSerializersMap[alternate]; 360 } 361 } 362 } 363 364 if (serializer != null) 365 { 366 return serializer.apply(null, [value, keep_refs]); 367 } 368 else 369 { 370 if (retrieveCachedTypeDescription(className, false) == null) 371 { 372 try 373 { 374 result.newTypes.push(retrieveCachedTypeDescription(className, true)); 375 } 376 catch(err:Error) 377 { 378 var interfaceInfo:XMLList = describeType(value).implementsInterface; 379 for each (var interf:XML in interfaceInfo) 380 { 381 className = interf.@type.toString(); 382 if (retrieveCachedTypeDescription(className, false) == null){ 383 result.newTypes.push(retrieveCachedTypeDescription(className, true)); 384 } //end if push new data type 385 386 } //end for going through interfaces 387 var baseClass:String = describeType(value).@base.toString(); 388 if (retrieveCachedTypeDescription(baseClass, false) == null){ 389 result.newTypes.push(retrieveCachedTypeDescription(baseClass, true)); 390 } //end if push new data type 391 } 392 } 393 394 // make sure the reference is known 395 var objRef:Number = getRef(value, false); 396 var should_keep_ref:Boolean = false; 397 if (isNaN(objRef)) 398 { 399 //create the reference if necessary 400 objRef = getRef(value, true); 401 should_keep_ref = true; 402 } 403 404 result.newRefs[objRef] = className; 405 //trace("serializing new reference: " + className + " with value" + value); 406 407 //the result is a getProperty / invokeMethod call. How can we know how much you will need the object ? 408 if (keep_refs && should_keep_ref) { 409 incRef(objRef); 410 } 411 result.value = objRef; 412 } 413 } 414 return result; 415 } 416 417 /** 418 * deserializes a value passed in from javascript. See serialize for details on how values are packed and 419 * unpacked for transfer across the bridge. 420 */ 421 public function deserialize(valuePackage:*):* 422 { 423 var result:*; 424 if (valuePackage is Number || valuePackage is Boolean || valuePackage is String || valuePackage === null || valuePackage === undefined || valuePackage is int || valuePackage is uint) 425 { 426 result = valuePackage; 427 } 428 else if(valuePackage is Array) 429 { 430 result = []; 431 for (var i:int = 0; i < valuePackage.length; i++) 432 { 433 result[i] = deserialize(valuePackage[i]); 434 } 435 } 436 else if (valuePackage.type == FABridge.TYPE_JSFUNCTION) 437 { 438 result = getRemoteFunctionProxy(valuePackage.value, true); 439 } 440 else if (valuePackage.type == FABridge.TYPE_ASFUNCTION) 441 { 442 throw new Error("as functions can't be passed back to as yet"); 443 } 444 else if (valuePackage.type == FABridge.TYPE_ASINSTANCE) 445 { 446 result = resolveRef(valuePackage.value); 447 } 448 else if (valuePackage.type == FABridge.TYPE_ANONYMOUS) 449 { 450 result = valuePackage.value; 451 } 452 return result; 453 } 454 455 public function addCustomSerialization(className:String, serializationFunction:Function):void 456 { 457 customSerializersMap[className] = serializationFunction; 458 } 459 460 461 // type management 462 463 /** 464 * retrieves a type description for the type indicated by className, building one and caching it if necessary 465 */ 466 public function retrieveCachedTypeDescription(className:String, createifNecessary:Boolean):Object 467 { 468 if(localTypeMap[className] == null && createifNecessary == true) 469 { 470 localTypeMap[className] = buildTypeDescription(className); 471 } 472 return localTypeMap[className]; 473 } 474 475 public function addCachedTypeDescription(className:String, desc:Object):Object 476 { 477 if (localTypeMap[className] == null) 478 { 479 localTypeMap[className] = desc; 480 } 481 return localTypeMap[className]; 482 } 483 484 /** 485 * builds a type description for the type indiciated by className 486 */ 487 public function buildTypeDescription(className:String):Object 488 { 489 var desc:Object = {}; 490 491 className = className.replace(/::/,"."); 492 493 var objClass:Class = Class(ApplicationDomain.currentDomain.getDefinition(className)); 494 495 var xData:XML = describeType(objClass); 496 497 desc.name = xData.@name.toString(); 498 499 var methods:Array = []; 500 var xMethods:XMLList = xData.factory.method; 501 for (var i:int = 0; i < xMethods.length(); i++) 502 { 503 methods.push(xMethods[i].@name.toString()); 504 } 505 desc.methods = methods; 506 507 var accessors:Array = []; 508 var xAcc:XMLList = xData.factory.accessor; 509 for (i = 0; i < xAcc.length(); i++) 510 { 511 accessors.push(xAcc[i].@name.toString()); 512 } 513 xAcc = xData.factory.variable; 514 for (i = 0; i < xAcc.length(); i++) 515 { 516 accessors.push(xAcc[i].@name.toString()); 517 } 518 desc.accessors = accessors; 519 520 return desc; 521 } 522 523// instance mgmt 524 525 /** 526 * resolves an instance id passed from JS to an instance previously cached for representing in JS 527 */ 528 private function resolveRef(objRef:Number):Object 529 { 530 try 531 { 532 return (objRef == -1)? baseObject : localInstanceMap[objRef]; 533 } 534 catch(e:Error) 535 { 536 return serialize("__FLASHERROR__"+"||"+e.message); 537 } 538 539 return (objRef == -1)? baseObject : localInstanceMap[objRef]; 540 } 541 542 /** 543 * returns an id associated with the object provided for passing across the bridge to JS 544 */ 545 public function getRef(obj:Object, createIfNecessary:Boolean):Number 546 { 547 try 548 { 549 var ref:Number; 550 551 if (createIfNecessary) 552 { 553 var newRef:Number = nextID++; 554 localInstanceMap[newRef] = obj; 555 ref = newRef; 556 } 557 else 558 { 559 for (var key:* in localInstanceMap) 560 { 561 if (localInstanceMap[key] === obj) 562 { 563 ref = key; 564 break; 565 } 566 } 567 } 568 } 569 catch(e:Error) 570 { 571 return serialize("__FLASHERROR__"+"||"+e.message) 572 } 573 574 return ref; 575 } 576 577 578 // function management 579 580 /** 581 * resolves a function ID passed from JS to a local function previously cached for representation in JS 582 */ 583 private function resolveFunctionID(funcID:Number):Function 584 { 585 return localFunctionMap[funcID]; 586 } 587 588 /** 589 * associates a unique ID with a local function suitable for passing across the bridge to proxy in Javascript 590 */ 591 public function getFunctionID(f:Function, createIfNecessary:Boolean):Number 592 { 593 var ref:Number; 594 595 if (createIfNecessary) 596 { 597 var newID:Number = nextID++; 598 localFunctionMap[newID] = f; 599 ref = newID; 600 } 601 else 602 { 603 for (var key:* in localFunctionMap) 604 { 605 if (localFunctionMap[key] === f) { 606 ref = key; 607 } 608 break; 609 } 610 } 611 612 return ref; 613 } 614 615 /** 616 * returns a proxy function that represents a function defined in javascript. This function can be called syncrhonously, and will 617 * return any values returned by the JS function 618 */ 619 public function getRemoteFunctionProxy(functionID:Number, createIfNecessary:Boolean):Function 620 { 621 try 622 { 623 if (remoteFunctionCache[functionID] == null) 624 { 625 remoteFunctionCache[functionID] = function(...args):* 626 { 627 var externalArgs:Array = args.concat(); 628 externalArgs.unshift(functionID); 629 var serializedArgs:* = serialize(externalArgs, true); 630 631 if(checkToThrowLater(serializedArgs[1])) 632 { 633 setTimeout(function a():* { 634 try { 635 var retVal:* = ExternalInterface.call("FABridge__invokeJSFunction", serializedArgs); 636 for(var i:int = 0; i<serializedArgs.length; i++) 637 { 638 if(typeof(serializedArgs[i]) == "object" && serializedArgs[i]!=null) 639 { 640 releaseRef(serializedArgs[i].value); 641 } 642 } 643 return retVal; 644 } 645 catch(e:Error) 646 { 647 return serialize("__FLASHERROR__"+"||"+e.message); 648 } 649 },1); 650 } 651 else 652 { 653 var retVal:* = ExternalInterface.call("FABridge__invokeJSFunction", serializedArgs); 654 for(var i:int = 0; i<serializedArgs.length; i++) 655 { 656 if(typeof(serializedArgs[i]) == "object" && serializedArgs[i]!=null) 657 { 658 releaseRef(serializedArgs[i].value); 659 } 660 } 661 return retVal; 662 } 663 } 664 } 665 } 666 catch(e:Error) 667 { 668 return serialize("__FLASHERROR__"+"||"+e.message); 669 } 670 671 return remoteFunctionCache[functionID]; 672 } 673 674 /** 675 * function that checks if the object on which we are working demands that it should be called at a later time, breaking the call chain 676 * we check the actual object, as well as the bsae class and interfaces 677 */ 678 private function checkToThrowLater(obj:Object):Boolean 679 { 680 obj = resolveRef(obj.value); 681 var className:String = getQualifiedClassName(obj); 682 var classInfo:XML = describeType(obj); 683 684 if (FABridge.EventsToCallLater[className] != null) { 685 return true; 686 } 687 688 //check if this class doesn't inherit from one of the entries in the table 689 var inheritanceInfo:XMLList = describeType(obj).extendsClass; 690 for each (var inherit:XML in inheritanceInfo) 691 { 692 className = inherit.@type.toString(); 693 if (FABridge.EventsToCallLater[className] != null) { 694 return true; 695 } 696 } //end for going through inheritance tree 697 698 //if we're still here, check the interfaces as well 699 700 var interfaceInfo:XMLList = describeType(obj).implementsInterface; 701 for each (var interf:XML in interfaceInfo) 702 { 703 className = interf.@type.toString(); 704 if (FABridge.EventsToCallLater[className] != null) { 705 return true; 706 } 707 } //end for going through inheritance tree 708 709 //if nothing was found, return false, so the function gets executed 710 return false; 711 } 712 713 // callbacks exposed to JS 714 715 /** 716 * called to fetch a named property off the instanced associated with objID 717 */ 718 public function js_getPropFromAS(objID:Number, propName:String):* 719 { 720 incRef(objID); 721 try 722 { 723 var obj:Object = resolveRef(objID); 724 var ret:* = serialize(obj[propName], true); 725 releaseRef(objID); 726 return ret; 727 } 728 catch (e:ItemPendingError) 729 { 730 releaseRef(objID); 731 //ItemPendingError 732 //return serialize("an error occcured with" + obj[propName]); 733 } 734 catch(e:Error) 735 { 736 releaseRef(objID); 737 return serialize("__FLASHERROR__" + "||" + e.message); 738 } 739 } 740 741 /** 742 * called to set a named property on the instance associated with objID 743 */ 744 private function js_setPropertyInAS(objID:Number, propRef:String, value:*):* 745 { 746 incRef(objID); 747 try { 748 var obj:Object = resolveRef(objID); 749 obj[propRef] = deserialize(value); 750 releaseRef(objID); 751 } 752 catch(e:Error) 753 { 754 releaseRef(objID); 755 return serialize("__FLASHERROR__" + "||" + e.message); 756 } 757 } 758 759 /** 760 * accessor for retrieveing a proxy to the root object from JS 761 */ 762 private function js_getRoot():* 763 { 764 try 765 { 766 //always get the root; this is the same as the get property, only it is the root object 767 var objRef:Number = getRef(baseObject, false); 768 if (isNaN(objRef)) 769 { 770 //create the reference if necessary 771 objRef = getRef(baseObject, true); 772 incRef(objRef); 773 } 774 return serialize(baseObject); 775 } 776 catch(e:Error) 777 { 778 return serialize("__FLASHERROR__"+"||"+e.message); 779 } 780 } 781 782 /** 783 * called to invoke a function or closure associated with funcID 784 */ 785 private function js_invokeFunction(funcID:Number, args:Object):* 786 { 787 var result:*; 788 try 789 { 790 var func:Function = resolveFunctionID(funcID); 791 if(func != null) 792 result = func.apply(null, deserialize(args)); 793 794 return serialize(result, true); 795 } 796 catch(e:Error) 797 { 798 return serialize("__FLASHERROR__"+"||"+e.message); 799 } 800 } 801 802 /** 803 * called to invoke a named method on the object associated with objID 804 */ 805 private function js_invokeMethod(objID:Number, methodName:String, args:Object):* 806 { 807 incRef(objID); 808 try 809 { 810 var obj:Object = resolveRef(objID); 811 var result:*; 812 813 //check if the method is callable right now, or later 814 var callLater:Boolean = checkToExecuteLater(obj, methodName); 815 816 if (callLater) { 817 var t:Timer = new Timer(200, 1); 818 t.addEventListener(TimerEvent.TIMER, function():void { 819 var ret_inner:* = serialize(obj[methodName].apply(null, deserialize(args)), true); 820 releaseRef(objID); 821 }); 822 t.start(); 823 } else { 824 var ret:* = serialize(obj[methodName].apply(null, deserialize(args)), true); 825 releaseRef(objID); 826 return ret; 827 } 828 } 829 catch (e:ItemPendingError) 830 { 831 releaseRef(objID); 832 // ignore ItemPendingError 833 } 834 catch(e:Error) 835 { 836 releaseRef(objID); 837 return serialize("__FLASHERROR__" + "||" + e.message); 838 } 839 } 840 841 /** 842 * method that performs a check on the specified object and method to see if their execution should be delayed or not 843 * it checks the object, its base class and implemented interfaces 844 */ 845 private function checkToExecuteLater(obj:Object, methodName:String):Boolean 846 { 847 var methods:String; 848 var className:String = getQualifiedClassName(obj); 849 var classInfo:XML = describeType(obj); 850 851 if (FABridge.MethodsToCallLater[className] != null) { 852 methods = FABridge.MethodsToCallLater[className]; 853 //must call later 854 if(methods.match(methodName)) 855 { 856 return true; 857 } 858 } 859 860 //check if this class doesn't inherit from one of the entries in the table 861 var inheritanceInfo:XMLList = describeType(obj).extendsClass; 862 for each (var inherit:XML in inheritanceInfo) 863 { 864 className = inherit.@type.toString(); 865 if (FABridge.MethodsToCallLater[className] != null) { 866 methods = FABridge.MethodsToCallLater[className]; 867 //must call later 868 if(methods.match(methodName)) 869 { 870 return true; 871 } 872 } 873 } //end for going through inheritance tree 874 875 //if we're still here, check the interfaces as well 876 877 var interfaceInfo:XMLList = describeType(obj).implementsInterface; 878 for each (var interf:XML in interfaceInfo) 879 { 880 className = interf.@type.toString(); 881 if (FABridge.MethodsToCallLater[className] != null) { 882 methods = FABridge.MethodsToCallLater[className]; 883 //must call later 884 if(methods.match(methodName)) 885 { 886 return true; 887 } 888 } 889 } //end for going through inheritance tree 890 891 //if nothing was found, return false, so the function gets executed 892 return false; 893 } 894 895 /** 896 * callback from JS to release all AS Objects from the local cache maps 897 */ 898 private function js_releaseASObjects():void 899 { 900 localTypeMap = new Dictionary(); 901 localInstanceMap = new Dictionary(); 902 localFunctionMap = new Dictionary(); 903 } 904 905 /** 906 * callback from JS to release a specific object, identified by its ID 907 */ 908 private function js_releaseNamedASObject(objId:int):Boolean 909 { 910 var retVal:Boolean = false; 911 if (localInstanceMap[objId] != null) 912 { 913 delete refMap[objId]; 914 delete localInstanceMap[objId]; 915 retVal = true; 916 } 917 return retVal; 918 } 919 920 /** 921 * callback for js to create a new class instance. 922 */ 923 924 private function js_create(className:String):* 925 { 926 try 927 { 928 var c:Class = Class(ApplicationDomain.currentDomain.getDefinition(className)); 929 var instance:Object = new c(); 930 } 931 catch(e:Error) 932 { 933 return serialize("__FLASHERROR__" + "||" + e.message); 934 } 935 936 // make sure the reference is known 937 var objRef:Number = getRef(instance, true); 938 incRef(objRef); 939 return serialize(instance); 940 } 941 942} 943} 944