Products
Technologies

Developer resources

Build a JavaScript API for your Fireworks Flash panel

Avg. Rating 4.5

Problem

Passing complicated strings of JavaScript statements from a Flash panel to the Fireworks API via the MMExecute function is awkward and error-prone.

Solution

Encapsulate the complicated JavaScript code in a simpler API that your Flash panel can call.

Detailed explanation

When building Flash dialogs or panels for Fireworks, you use MMExecute() to execute strings of JavaScript to manipulate the DOM. In most tutorials for writing panels, you see long concatenated bits of code, like:

MMExecute('fw.getDocumentDOM.setSelectionBounds({ left: ' + x + ', top: ' + y + ', right: ' + (x + width) + ', bottom: ' + (y + top) + ' })');

This is painful to read, write and debug, but there's a much better approach. Instead of making multiple MMExecute calls, one for each statement, it's much more convenient to create a JS interface that your ActionScript can call. Once you create a global variable in Fireworks' JavaScript interpreter, it sticks around in memory until you quit Fireworks. You can use this persistence to build an interface that will be available to your ActionScript code.

First, pick a unique global variable name to encapsulate the methods of your API, and then set it to an object containing as many methods as you need. For example:

MyPanelAPI = {
    resizeSelectionBy: function(l, t, r, b)
    {
        var dom = fw.getDocumenDOM();

        if (fw.selection.length) {
            var bounds = dom.getSelectionBounds();
            dom.setSelectionBounds({ 
                left: bounds.left + l, 
                top: bounds.top + t, 
                right: bounds.right + r, 
                bottom: bounds.bottom + b 
            });
        }
    },
    
    doSomethingElse: function(a, b)
    {
        ...
    }
};

After this code has been executed once, your ActionScript code can make calls to your API like:

MMExecute("MyPanelAPI.resizeSelectionBy(" + [-10, -10, 10, 10].join(",") + ")");

This expands the selection by 10px in each direction. Calling the method in this way should be a lot easier to write and maintain than bundling all of resizeSelectionBy's code into one big string. It also ensures that only one step appears in the History panel for each method (each call to MMExecute generates its own history step). Another important advantage is ease of testing: you can create a .jsf file that instantiates your JavaScript API and then calls its methods. You can run this test command directly from the Commands menu, allowing you to quickly iterate on your panel's functionality without having to recompile anything in Flash.

How do you pass the API code to Fireworks to define the API? You could manually turn it into a big string, though escaping all the quotes is error-prone. You could also set it as the value of a text field in Flash, and then pass the field's text property to MMExecute().

In AS3, a better way is to actually keep the JS in a separate external .js file. You can subclass flash.utils.ByteArray to create a very simple class representing your API and then use an embed directive to embed the external .js file into your SWF. For instance, the MyPanelAPI.as file would look like:

package {
    import flash.utils.ByteArray;  

    [Embed(source="MyPanelAPI.js", 
        mimeType="application/octet-stream")]
    public class MyPanelAPI extends ByteArray
    {
    }
}

The class here is so simple it's actually empty; it's needed only to embed the external file. Put the MyPanelAPI.as file in your project's source directory so the rest of your code can find it. To instantiate your API on the JavaScript side, pass an instance of your API class to MMExecute:

MMExecute((new MyPanelAPI()).toString());

You should need to instantiate your API only once, when the Flash panel loads.

If you like, you can further simplify things by encapsulating the calls to MMExecute() in your own method. Add the following callMethod() method to your application's class definition. (Note that you'll also need to add the as3corelib.swc file to your AS3 project, since the method below uses JSON serialization):

    // this library is available from 
    // http://code.google.com/p/as3corelib/
import com.adobe.serialization.json.*;

private function callMethod(
    inMethodName:String,
    ...inArgs) : String
{
        // inArgs will be an array containing all of the arguments 
        // after the first one.  convert that array to a JSON 
        // string representation and then strip the [ ] from the 
        // string, since we'll be using it as a list of method 
        // parameters, not as an array.
    var argString:String = JSON.encode(inArgs).slice(1, -1);

        // include a semi-colon at the end so that the command 
        // history steps have them
    var js:String = inMethodName + "(" + argString + ");";
    
    return MMExecute(js);
}

With this method, it's easy to pass objects or arrays into your API. For example, say you have a setVisibility() method in your API that takes a boolean and an array of strings containing the names of layers to hide or show. You can call your method without having to do any string manipulation using callMethod:

callMethod("MyPanelAPI.setVisibility", false, ["Layer1", "Layer2", "Background"]);

callMethod automatically converts this to a call to MMExecute like:

MMExecute('MyPanelAPI.setVisibility(false, ["Layer1", "Layer2", "Background"])');

Hopefully, this approach will help you create more complex Flash panels for Fireworks with less hassle.


+
This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License. Permissions beyond the scope of this license, pertaining to the examples of code included within this work are available at Adobe.

Report abuse

Related recipes