| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // Licensed to the Apache Software Foundation (ASF) under one or more |
| // contributor license agreements. See the NOTICE file distributed with |
| // this work for additional information regarding copyright ownership. |
| // The ASF licenses this file to You under the Apache License, Version 2.0 |
| // (the "License"); you may not use this file except in compliance with |
| // the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| //////////////////////////////////////////////////////////////////////////////// |
| /* |
| * The Bridge class, responsible for navigating JS instances |
| */ |
| package bridge |
| { |
| |
| /* |
| * imports |
| */ |
| import flash.external.ExternalInterface; |
| import flash.utils.Timer; |
| import flash.events.*; |
| import flash.display.DisplayObject; |
| import flash.system.ApplicationDomain; |
| import flash.utils.Dictionary; |
| import flash.utils.setTimeout; |
| |
| import mx.collections.errors.ItemPendingError; |
| import mx.core.IMXMLObject; |
| |
| import flash.utils.getQualifiedClassName; |
| import flash.utils.describeType; |
| import flash.events.TimerEvent; |
| |
| /** |
| * The FABridge class, responsible for proxying AS objects into javascript |
| */ |
| public class FABridge extends EventDispatcher implements IMXMLObject |
| { |
| |
| //holds a list of stuff to call later, to break the recurrence of the js <> as calls |
| //you must use the full class name, as returned by the getQualifiedClassName() function |
| public static const MethodsToCallLater:Object = new Object(); |
| MethodsToCallLater["mx.collections::ArrayCollection"]="refresh,removeItemAt"; |
| |
| public static const EventsToCallLater:Object = new Object(); |
| EventsToCallLater["mx.data.events::UnresolvedConflictsEvent"]="true"; |
| EventsToCallLater["mx.events::PropertyChangeEvent"]="true"; |
| |
| public static const INITIALIZED:String = "bridgeInitialized"; |
| |
| // constructor |
| public function FABridge() |
| { |
| super(); |
| initializeCallbacks(); |
| } |
| |
| // private vars |
| |
| /** |
| * stores a cache of descriptions of AS types suitable for sending to JS |
| */ |
| private var localTypeMap:Dictionary = new Dictionary(); |
| |
| /** |
| * stores an id-referenced dictionary of objects exported to JS |
| */ |
| private var localInstanceMap:Dictionary = new Dictionary(); |
| |
| /** |
| * stores an id-referenced dictionary of functions exported to JS |
| */ |
| private var localFunctionMap:Dictionary = new Dictionary(); |
| |
| /** |
| * stores an id-referenced dictionary of proxy functions imported from JS |
| */ |
| private var remoteFunctionCache:Dictionary = new Dictionary(); |
| |
| /** |
| * stores a list of custom serialization functions |
| */ |
| private var customSerializersMap:Dictionary = new Dictionary(); |
| |
| /** |
| * stores a map of object ID's and their reference count |
| */ |
| private var refMap:Dictionary = new Dictionary(); |
| /** |
| * a local counter for generating unique IDs |
| */ |
| private var nextID:Number = 0; |
| |
| private var lastRef:int; |
| |
| /* values that can't be serialized natively across the bridge are packed and identified by type. |
| These constants represent different serialization types */ |
| public static const TYPE_ASINSTANCE:uint = 1; |
| public static const TYPE_ASFUNCTION:uint = 2; |
| public static const TYPE_JSFUNCTION:uint = 3; |
| public static const TYPE_ANONYMOUS:uint = 4; |
| |
| private var _initChecked:Boolean = false; |
| |
| // properties |
| |
| //getters and setters for the main component in the swf - the root |
| public function get rootObject():DisplayObject {return _rootObject;} |
| public function set rootObject(value:DisplayObject):void |
| { |
| _rootObject = value; |
| checkInitialized(); |
| } |
| |
| /** |
| * the bridge name |
| */ |
| public var bridgeName:String; |
| private var _registerComplete:Boolean = false; |
| |
| /** |
| * increment the reference count for an object being passed over the bridge |
| */ |
| public function incRef(objId:int):void |
| { |
| if(refMap[objId] == null) { |
| //the object is being created; we now add it to the map and set its refCount = 1 |
| refMap[objId] = 1; |
| } else { |
| refMap[objId] = refMap[objId] +1; |
| } |
| } |
| |
| /** |
| * when an object has been completely passed to JS its reference count is decreased with 1 |
| */ |
| public function releaseRef(objId:int):void |
| { |
| if(refMap[objId] != null) |
| { |
| var newRefVal:int = refMap[objId] - 1; |
| // if the object exists in the referenceMap and its count equals or has dropped under 0 we clean it up |
| if(refMap[objId] != null && newRefVal <= 0) |
| { |
| delete refMap[objId]; |
| delete localInstanceMap[objId]; |
| } |
| else |
| { |
| refMap[objId] = newRefVal; |
| } |
| } |
| } |
| |
| /** |
| * attaches the callbacks to external interface |
| */ |
| public function initializeCallbacks():void |
| { |
| if (ExternalInterface.available == false) |
| { |
| return; |
| } |
| |
| ExternalInterface.addCallback("getRoot", js_getRoot); |
| ExternalInterface.addCallback("getPropFromAS", js_getPropFromAS); |
| ExternalInterface.addCallback("setPropInAS", js_setPropertyInAS); |
| ExternalInterface.addCallback("invokeASMethod", js_invokeMethod); |
| ExternalInterface.addCallback("invokeASFunction", js_invokeFunction); |
| ExternalInterface.addCallback("releaseASObjects", js_releaseASObjects); |
| ExternalInterface.addCallback("create", js_create); |
| ExternalInterface.addCallback("releaseNamedASObject",js_releaseNamedASObject); |
| ExternalInterface.addCallback("incRef", incRef); |
| ExternalInterface.addCallback("releaseRef", releaseRef); |
| } |
| |
| private var _rootObject:DisplayObject; |
| |
| private var _document:DisplayObject; |
| |
| /** |
| * called to check whether the bridge has been initialized for the specified document/id pairs |
| */ |
| public function initialized(document:Object, id:String):void |
| { |
| _document = (document as DisplayObject); |
| |
| if (_document != null) |
| { |
| checkInitialized(); |
| } |
| } |
| |
| private function get baseObject():DisplayObject |
| { |
| return (rootObject == null)? _document:rootObject; |
| } |
| |
| |
| private function checkInitialized():void |
| { |
| if(_initChecked== true) |
| { |
| return; |
| } |
| _initChecked = true; |
| |
| // oops! timing error. Player team is working on it. |
| var t:Timer = new Timer(200,1); |
| t.addEventListener(TimerEvent.TIMER,auxCheckInitialized); |
| t.start(); |
| } |
| |
| /** |
| * auxiliary initialization check that is called after the timing has occurred |
| */ |
| private function auxCheckInitialized(e:Event):void |
| { |
| |
| var bCanGetParams:Boolean = true; |
| |
| try |
| { |
| var params:Object = baseObject.root.loaderInfo.parameters; |
| } |
| catch (e:Error) |
| { |
| bCanGetParams = false; |
| } |
| |
| if (bCanGetParams == false) |
| { |
| var t:Timer = new Timer(100); |
| var timerFunc:Function = function(e:TimerEvent):void |
| { |
| if(baseObject.root != null) |
| { |
| try |
| { |
| bCanGetParams = true; |
| var params:Object = baseObject.root.loaderInfo.parameters; |
| } |
| catch (err:Error) |
| { |
| bCanGetParams = false; |
| } |
| if (bCanGetParams) |
| { |
| t.removeEventListener(TimerEvent.TIMER, timerFunc); |
| t.stop(); |
| dispatchInit(); |
| } |
| } |
| } |
| t.addEventListener(TimerEvent.TIMER, timerFunc); |
| t.start(); |
| } |
| else |
| { |
| dispatchInit(); |
| } |
| } |
| |
| /** |
| * call into JS to annunce that the bridge is ready to be used |
| */ |
| private function dispatchInit(e:Event = null):void |
| { |
| if(_registerComplete == true) |
| { |
| return; |
| } |
| |
| if (ExternalInterface.available == false) |
| { |
| return; |
| } |
| |
| if (bridgeName == null) |
| { |
| bridgeName = baseObject.root.loaderInfo.parameters["bridgeName"]; |
| |
| if(bridgeName == null) |
| { |
| bridgeName = "flash"; |
| } |
| } |
| |
| _registerComplete = ExternalInterface.call("FABridge__bridgeInitialized", [bridgeName]); |
| dispatchEvent(new Event(FABridge.INITIALIZED)); |
| } |
| |
| // serialization/deserialization |
| |
| /** serializes a value for transfer across the bridge. primitive types are left as is. Arrays are left as arrays, but individual |
| * values in the array are serialized according to their type. Functions and class instances are inserted into a hash table and sent |
| * across as keys into the table. |
| * |
| * 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, |
| * 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 |
| * across, a descriptor indicating methods, properties, and variables of the type is also sent across |
| */ |
| public function serialize(value:*, keep_refs:Boolean=false):* |
| { |
| var result:* = {}; |
| result.newTypes = []; |
| result.newRefs = {}; |
| |
| if (value is Number || value is Boolean || value is String || value == null || value == undefined || value is int || value is uint) |
| { |
| result = value; |
| } |
| else if (value is Array) |
| { |
| result = []; |
| for(var i:int = 0; i < value.length; i++) |
| { |
| result[i] = serialize(value[i], keep_refs); |
| } |
| } |
| else if (value is Function) |
| { |
| // serialize a class |
| result.type = TYPE_ASFUNCTION; |
| result.value = getFunctionID(value, true); |
| } |
| else if (getQualifiedClassName(value) == "Object") |
| { |
| result.type = TYPE_ANONYMOUS; |
| result.value = value; |
| } |
| else |
| { |
| // serialize a class |
| result.type = TYPE_ASINSTANCE; |
| // make sure the type info is available |
| var className:String = getQualifiedClassName(value); |
| |
| var serializer:Function = customSerializersMap[className]; |
| |
| // try looking up the serializer under an alternate name |
| if (serializer == null) |
| { |
| if (className.indexOf('$') > 0) |
| { |
| var split:int = className.lastIndexOf(':'); |
| if (split > 0) |
| { |
| var alternate:String = className.substring(split+1); |
| serializer = customSerializersMap[alternate]; |
| } |
| } |
| } |
| |
| if (serializer != null) |
| { |
| return serializer.apply(null, [value, keep_refs]); |
| } |
| else |
| { |
| if (retrieveCachedTypeDescription(className, false) == null) |
| { |
| try |
| { |
| result.newTypes.push(retrieveCachedTypeDescription(className, true)); |
| } |
| catch(err:Error) |
| { |
| var interfaceInfo:XMLList = describeType(value).implementsInterface; |
| for each (var interf:XML in interfaceInfo) |
| { |
| className = interf.@type.toString(); |
| if (retrieveCachedTypeDescription(className, false) == null){ |
| result.newTypes.push(retrieveCachedTypeDescription(className, true)); |
| } //end if push new data type |
| |
| } //end for going through interfaces |
| var baseClass:String = describeType(value).@base.toString(); |
| if (retrieveCachedTypeDescription(baseClass, false) == null){ |
| result.newTypes.push(retrieveCachedTypeDescription(baseClass, true)); |
| } //end if push new data type |
| } |
| } |
| |
| // make sure the reference is known |
| var objRef:Number = getRef(value, false); |
| var should_keep_ref:Boolean = false; |
| if (isNaN(objRef)) |
| { |
| //create the reference if necessary |
| objRef = getRef(value, true); |
| should_keep_ref = true; |
| } |
| |
| result.newRefs[objRef] = className; |
| //trace("serializing new reference: " + className + " with value" + value); |
| |
| //the result is a getProperty / invokeMethod call. How can we know how much you will need the object ? |
| if (keep_refs && should_keep_ref) { |
| incRef(objRef); |
| } |
| result.value = objRef; |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * deserializes a value passed in from javascript. See serialize for details on how values are packed and |
| * unpacked for transfer across the bridge. |
| */ |
| public function deserialize(valuePackage:*):* |
| { |
| var result:*; |
| if (valuePackage is Number || valuePackage is Boolean || valuePackage is String || valuePackage === null || valuePackage === undefined || valuePackage is int || valuePackage is uint) |
| { |
| result = valuePackage; |
| } |
| else if(valuePackage is Array) |
| { |
| result = []; |
| for (var i:int = 0; i < valuePackage.length; i++) |
| { |
| result[i] = deserialize(valuePackage[i]); |
| } |
| } |
| else if (valuePackage.type == FABridge.TYPE_JSFUNCTION) |
| { |
| result = getRemoteFunctionProxy(valuePackage.value, true); |
| } |
| else if (valuePackage.type == FABridge.TYPE_ASFUNCTION) |
| { |
| throw new Error("as functions can't be passed back to as yet"); |
| } |
| else if (valuePackage.type == FABridge.TYPE_ASINSTANCE) |
| { |
| result = resolveRef(valuePackage.value); |
| } |
| else if (valuePackage.type == FABridge.TYPE_ANONYMOUS) |
| { |
| result = valuePackage.value; |
| } |
| return result; |
| } |
| |
| public function addCustomSerialization(className:String, serializationFunction:Function):void |
| { |
| customSerializersMap[className] = serializationFunction; |
| } |
| |
| |
| // type management |
| |
| /** |
| * retrieves a type description for the type indicated by className, building one and caching it if necessary |
| */ |
| public function retrieveCachedTypeDescription(className:String, createifNecessary:Boolean):Object |
| { |
| if(localTypeMap[className] == null && createifNecessary == true) |
| { |
| localTypeMap[className] = buildTypeDescription(className); |
| } |
| return localTypeMap[className]; |
| } |
| |
| public function addCachedTypeDescription(className:String, desc:Object):Object |
| { |
| if (localTypeMap[className] == null) |
| { |
| localTypeMap[className] = desc; |
| } |
| return localTypeMap[className]; |
| } |
| |
| /** |
| * builds a type description for the type indiciated by className |
| */ |
| public function buildTypeDescription(className:String):Object |
| { |
| var desc:Object = {}; |
| |
| className = className.replace(/::/,"."); |
| |
| var objClass:Class = Class(ApplicationDomain.currentDomain.getDefinition(className)); |
| |
| var xData:XML = describeType(objClass); |
| |
| desc.name = xData.@name.toString(); |
| |
| var methods:Array = []; |
| var xMethods:XMLList = xData.factory.method; |
| for (var i:int = 0; i < xMethods.length(); i++) |
| { |
| methods.push(xMethods[i].@name.toString()); |
| } |
| desc.methods = methods; |
| |
| var accessors:Array = []; |
| var xAcc:XMLList = xData.factory.accessor; |
| for (i = 0; i < xAcc.length(); i++) |
| { |
| accessors.push(xAcc[i].@name.toString()); |
| } |
| xAcc = xData.factory.variable; |
| for (i = 0; i < xAcc.length(); i++) |
| { |
| accessors.push(xAcc[i].@name.toString()); |
| } |
| desc.accessors = accessors; |
| |
| return desc; |
| } |
| |
| // instance mgmt |
| |
| /** |
| * resolves an instance id passed from JS to an instance previously cached for representing in JS |
| */ |
| private function resolveRef(objRef:Number):Object |
| { |
| try |
| { |
| return (objRef == -1)? baseObject : localInstanceMap[objRef]; |
| } |
| catch(e:Error) |
| { |
| return serialize("__FLASHERROR__"+"||"+e.message); |
| } |
| |
| return (objRef == -1)? baseObject : localInstanceMap[objRef]; |
| } |
| |
| /** |
| * returns an id associated with the object provided for passing across the bridge to JS |
| */ |
| public function getRef(obj:Object, createIfNecessary:Boolean):Number |
| { |
| try |
| { |
| var ref:Number; |
| |
| if (createIfNecessary) |
| { |
| var newRef:Number = nextID++; |
| localInstanceMap[newRef] = obj; |
| ref = newRef; |
| } |
| else |
| { |
| for (var key:* in localInstanceMap) |
| { |
| if (localInstanceMap[key] === obj) |
| { |
| ref = key; |
| break; |
| } |
| } |
| } |
| } |
| catch(e:Error) |
| { |
| return serialize("__FLASHERROR__"+"||"+e.message) |
| } |
| |
| return ref; |
| } |
| |
| |
| // function management |
| |
| /** |
| * resolves a function ID passed from JS to a local function previously cached for representation in JS |
| */ |
| private function resolveFunctionID(funcID:Number):Function |
| { |
| return localFunctionMap[funcID]; |
| } |
| |
| /** |
| * associates a unique ID with a local function suitable for passing across the bridge to proxy in Javascript |
| */ |
| public function getFunctionID(f:Function, createIfNecessary:Boolean):Number |
| { |
| var ref:Number; |
| |
| if (createIfNecessary) |
| { |
| var newID:Number = nextID++; |
| localFunctionMap[newID] = f; |
| ref = newID; |
| } |
| else |
| { |
| for (var key:* in localFunctionMap) |
| { |
| if (localFunctionMap[key] === f) { |
| ref = key; |
| } |
| break; |
| } |
| } |
| |
| return ref; |
| } |
| |
| /** |
| * returns a proxy function that represents a function defined in javascript. This function can be called syncrhonously, and will |
| * return any values returned by the JS function |
| */ |
| public function getRemoteFunctionProxy(functionID:Number, createIfNecessary:Boolean):Function |
| { |
| try |
| { |
| if (remoteFunctionCache[functionID] == null) |
| { |
| remoteFunctionCache[functionID] = function(...args):* |
| { |
| var externalArgs:Array = args.concat(); |
| externalArgs.unshift(functionID); |
| var serializedArgs:* = serialize(externalArgs, true); |
| |
| if(checkToThrowLater(serializedArgs[1])) |
| { |
| setTimeout(function a():* { |
| try { |
| var retVal:* = ExternalInterface.call("FABridge__invokeJSFunction", serializedArgs); |
| for(var i:int = 0; i<serializedArgs.length; i++) |
| { |
| if(typeof(serializedArgs[i]) == "object" && serializedArgs[i]!=null) |
| { |
| releaseRef(serializedArgs[i].value); |
| } |
| } |
| return retVal; |
| } |
| catch(e:Error) |
| { |
| return serialize("__FLASHERROR__"+"||"+e.message); |
| } |
| },1); |
| } |
| else |
| { |
| var retVal:* = ExternalInterface.call("FABridge__invokeJSFunction", serializedArgs); |
| for(var i:int = 0; i<serializedArgs.length; i++) |
| { |
| if(typeof(serializedArgs[i]) == "object" && serializedArgs[i]!=null) |
| { |
| releaseRef(serializedArgs[i].value); |
| } |
| } |
| return retVal; |
| } |
| } |
| } |
| } |
| catch(e:Error) |
| { |
| return serialize("__FLASHERROR__"+"||"+e.message); |
| } |
| |
| return remoteFunctionCache[functionID]; |
| } |
| |
| /** |
| * 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 |
| * we check the actual object, as well as the bsae class and interfaces |
| */ |
| private function checkToThrowLater(obj:Object):Boolean |
| { |
| obj = resolveRef(obj.value); |
| var className:String = getQualifiedClassName(obj); |
| var classInfo:XML = describeType(obj); |
| |
| if (FABridge.EventsToCallLater[className] != null) { |
| return true; |
| } |
| |
| //check if this class doesn't inherit from one of the entries in the table |
| var inheritanceInfo:XMLList = describeType(obj).extendsClass; |
| for each (var inherit:XML in inheritanceInfo) |
| { |
| className = inherit.@type.toString(); |
| if (FABridge.EventsToCallLater[className] != null) { |
| return true; |
| } |
| } //end for going through inheritance tree |
| |
| //if we're still here, check the interfaces as well |
| |
| var interfaceInfo:XMLList = describeType(obj).implementsInterface; |
| for each (var interf:XML in interfaceInfo) |
| { |
| className = interf.@type.toString(); |
| if (FABridge.EventsToCallLater[className] != null) { |
| return true; |
| } |
| } //end for going through inheritance tree |
| |
| //if nothing was found, return false, so the function gets executed |
| return false; |
| } |
| |
| // callbacks exposed to JS |
| |
| /** |
| * called to fetch a named property off the instanced associated with objID |
| */ |
| public function js_getPropFromAS(objID:Number, propName:String):* |
| { |
| incRef(objID); |
| try |
| { |
| var obj:Object = resolveRef(objID); |
| var ret:* = serialize(obj[propName], true); |
| releaseRef(objID); |
| return ret; |
| } |
| catch (e:ItemPendingError) |
| { |
| releaseRef(objID); |
| //ItemPendingError |
| //return serialize("an error occcured with" + obj[propName]); |
| } |
| catch(e:Error) |
| { |
| releaseRef(objID); |
| return serialize("__FLASHERROR__" + "||" + e.message); |
| } |
| } |
| |
| /** |
| * called to set a named property on the instance associated with objID |
| */ |
| private function js_setPropertyInAS(objID:Number, propRef:String, value:*):* |
| { |
| incRef(objID); |
| try { |
| var obj:Object = resolveRef(objID); |
| obj[propRef] = deserialize(value); |
| releaseRef(objID); |
| } |
| catch(e:Error) |
| { |
| releaseRef(objID); |
| return serialize("__FLASHERROR__" + "||" + e.message); |
| } |
| } |
| |
| /** |
| * accessor for retrieveing a proxy to the root object from JS |
| */ |
| private function js_getRoot():* |
| { |
| try |
| { |
| //always get the root; this is the same as the get property, only it is the root object |
| var objRef:Number = getRef(baseObject, false); |
| if (isNaN(objRef)) |
| { |
| //create the reference if necessary |
| objRef = getRef(baseObject, true); |
| incRef(objRef); |
| } |
| return serialize(baseObject); |
| } |
| catch(e:Error) |
| { |
| return serialize("__FLASHERROR__"+"||"+e.message); |
| } |
| } |
| |
| /** |
| * called to invoke a function or closure associated with funcID |
| */ |
| private function js_invokeFunction(funcID:Number, args:Object):* |
| { |
| var result:*; |
| try |
| { |
| var func:Function = resolveFunctionID(funcID); |
| if(func != null) |
| result = func.apply(null, deserialize(args)); |
| |
| return serialize(result, true); |
| } |
| catch(e:Error) |
| { |
| return serialize("__FLASHERROR__"+"||"+e.message); |
| } |
| } |
| |
| /** |
| * called to invoke a named method on the object associated with objID |
| */ |
| private function js_invokeMethod(objID:Number, methodName:String, args:Object):* |
| { |
| incRef(objID); |
| try |
| { |
| var obj:Object = resolveRef(objID); |
| var result:*; |
| |
| //check if the method is callable right now, or later |
| var callLater:Boolean = checkToExecuteLater(obj, methodName); |
| |
| if (callLater) { |
| var t:Timer = new Timer(200, 1); |
| t.addEventListener(TimerEvent.TIMER, function():void { |
| var ret_inner:* = serialize(obj[methodName].apply(null, deserialize(args)), true); |
| releaseRef(objID); |
| }); |
| t.start(); |
| } else { |
| var ret:* = serialize(obj[methodName].apply(null, deserialize(args)), true); |
| releaseRef(objID); |
| return ret; |
| } |
| } |
| catch (e:ItemPendingError) |
| { |
| releaseRef(objID); |
| // ignore ItemPendingError |
| } |
| catch(e:Error) |
| { |
| releaseRef(objID); |
| return serialize("__FLASHERROR__" + "||" + e.message); |
| } |
| } |
| |
| /** |
| * method that performs a check on the specified object and method to see if their execution should be delayed or not |
| * it checks the object, its base class and implemented interfaces |
| */ |
| private function checkToExecuteLater(obj:Object, methodName:String):Boolean |
| { |
| var methods:String; |
| var className:String = getQualifiedClassName(obj); |
| var classInfo:XML = describeType(obj); |
| |
| if (FABridge.MethodsToCallLater[className] != null) { |
| methods = FABridge.MethodsToCallLater[className]; |
| //must call later |
| if(methods.match(methodName)) |
| { |
| return true; |
| } |
| } |
| |
| //check if this class doesn't inherit from one of the entries in the table |
| var inheritanceInfo:XMLList = describeType(obj).extendsClass; |
| for each (var inherit:XML in inheritanceInfo) |
| { |
| className = inherit.@type.toString(); |
| if (FABridge.MethodsToCallLater[className] != null) { |
| methods = FABridge.MethodsToCallLater[className]; |
| //must call later |
| if(methods.match(methodName)) |
| { |
| return true; |
| } |
| } |
| } //end for going through inheritance tree |
| |
| //if we're still here, check the interfaces as well |
| |
| var interfaceInfo:XMLList = describeType(obj).implementsInterface; |
| for each (var interf:XML in interfaceInfo) |
| { |
| className = interf.@type.toString(); |
| if (FABridge.MethodsToCallLater[className] != null) { |
| methods = FABridge.MethodsToCallLater[className]; |
| //must call later |
| if(methods.match(methodName)) |
| { |
| return true; |
| } |
| } |
| } //end for going through inheritance tree |
| |
| //if nothing was found, return false, so the function gets executed |
| return false; |
| } |
| |
| /** |
| * callback from JS to release all AS Objects from the local cache maps |
| */ |
| private function js_releaseASObjects():void |
| { |
| localTypeMap = new Dictionary(); |
| localInstanceMap = new Dictionary(); |
| localFunctionMap = new Dictionary(); |
| } |
| |
| /** |
| * callback from JS to release a specific object, identified by its ID |
| */ |
| private function js_releaseNamedASObject(objId:int):Boolean |
| { |
| var retVal:Boolean = false; |
| if (localInstanceMap[objId] != null) |
| { |
| delete refMap[objId]; |
| delete localInstanceMap[objId]; |
| retVal = true; |
| } |
| return retVal; |
| } |
| |
| /** |
| * callback for js to create a new class instance. |
| */ |
| |
| private function js_create(className:String):* |
| { |
| try |
| { |
| var c:Class = Class(ApplicationDomain.currentDomain.getDefinition(className)); |
| var instance:Object = new c(); |
| } |
| catch(e:Error) |
| { |
| return serialize("__FLASHERROR__" + "||" + e.message); |
| } |
| |
| // make sure the reference is known |
| var objRef:Number = getRef(instance, true); |
| incRef(objRef); |
| return serialize(instance); |
| } |
| |
| } |
| } |