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