views:

107

answers:

1

A Flash (actually Flex) object is created on an ASP.Net page within an Update Panel using a modified version of the embedCallAC_FL_RunContent.js script so it can be written in dynamically. It is re-created with this script with each partial postback to that panel. There are also other Update Panels on the page.

With some postbacks (partial and full), External Interface calls such as $get('FlashObj').ExternalInterfaceFunc('arg1', 0, true); are prepared server-side and added to the page using ScriptManager.RegisterStartupScript. They're embedded in a function and stuffed into Sys.Application's load event, for example Sys.Application.add_load(funcContainingExternalInterfaceCalls).

The problem is that because the Flash object's state state may change with each partial postback, the Flash (Flex) object and/or External Interface may not be ready or even exist yet in the DOM when the JavaScript -> Flash External Interface call is made. It results in an "Object doesn't support this property or method" exception.

I have a working strategy to make the ExternalInterface calls immediately if Flash is ready or else queue them until such time that Flash announces its readiness.

//Called when the Flash object is initialized and can accept ExternalInterfaceCalls
var flashReady = false;
//Called by Flash when object is fully initialized
function setFlashReady() {
    flashReady = true;
    //Make any queued ExternalInterface calls, then dequeue
    while (extIntQueue.length > 0)
        (extIntQueue.shift())();   
}
var extIntQueue = [];
function callExternalInterface(flashObjName, funcName, args) {
    //reference to the wrapped ExternalInterface Call
    var wrapped = extWrap(flashObjName, funcName, args);
    //only procede with ExternalInterface call if the global flashReady variable has been set
    if (flashReady) {
        wrapped();
    }
    else {
        //queue the function so when flashReady() is called next, the function is called and the aruments are passed.
        extIntQueue.push(wrapped);
    }
}
//bundle ExtInt call and hold variables in a closure
function extWrap(flashObjName, funcName, args) {
        //put vars in closure
        return function() {
            var funcCall = '$get("' + flashObjName + '").' + funcName;
            eval(funcCall).apply(this, args);
        }
    }

I set the flashReady var to dirty whenever I update the Update Panel that contains the Flash (Flex) object.

ScriptManager.RegisterClientScriptBlock(parentContainer, parentContainer.GetType(), "flashReady", "flashReady = false;", true);

I'm pleased that I got it to work, but it feels like a hack. I am still on the learning curve with respect to concepts like closures why "eval()" is apparently evil, so I'm wondering if I'm violating some best practice or if this code should be improved, if so how? Thanks.

A: 

Unfortunately I've had to do workarounds very similar to what you're doing, both with checking to make sure Flash is ready to accept calls, and then queuing commands.

It's a known issue that calling multiple ExternalInterface calls in quick succession (closer than about 400 milliseconds apart) may result in some getting ignored or dropped.

I feel your pain!

liquidleaf