Tuesday, July 28, 2009

Creating a JSFL Class and Understanding JSFL in the Flash CS4 IDE

This discussion helps with:
  • Understanding caching of JSFL variables in the Flash CS4 IDE (*) (**).
  • Building an external JSFL file that creates the illusion of a class that can be included in other JSFL files.
  • Running a JSFL file (that uses a custom Utils object like a registered namespace) from a custom panel.
* For the following JSFL discussion and tutorial, I am working in the Flash CS4 IDE. Publish settings for my .fla documents are set to: Flash Player 8 and Actionscript 2.0.

** If you are using an older version of the Flash IDE (prior to CS4), these same methods may not apply. If you are getting errors when trying to access variables from Flash's cache (as explained below), try defining variables in the external JSFL files as global. To define as global, remove the 'var' statement before your variable declarations.


Requirements Leading me to include a JSFL Class in a JSFL file:

I’ve been using JSFL to perform tasks in the Flash IDE, such as building objects in a document’s library and placing objects on a document’s Stage. JSFL is very tricky to implement properly, and it requires a lot of error catching even for very simple commands. For example, finding the current document (fl.getDocumentDOM()) will create an error if there is no Flash document open. Also, the JSFL functionality can change in every new version of Flash (JSFL commands might become deprecated, Flash might store values differently in its memory different, etc). I soon realized the need for a Utils class that I can include in my JSFL scripts and use it to perform common tasks. All of my JSFL files would include this class and would use functions defined within it to perform each task. However, JSFL does not have a #include directive like JavaScript does, so including an actual class will not work.

To include a Utils class in a JSFL file and use it like a registered namespace, I had to figure out how JSFL works in CS4, and determine what is possible:
  • Understand how Flash CS4 IDE caches in its memory any variables that are defined in a JSFL file.
  • Create a Utils Object in a JSFL file that can be used like a registered namespace (class) in other JSFL files.

Understanding how the Flash CS4 IDE caches in its memory any variables that are defined in a JSFL file:


Adobe has documentation for JSFL in Flash Help > Extending Flash. The documentation, however, does not explain how JSFL runs in Flash or how the Flash CS4 IDE (or any previous Flash IDE) stores JSFL variables in its memory.

I performed the following test in order to understand cacheing of JSFL variables in the Flash CS4 IDE:

Build a JSFL file that creates a variable and sets its value. Create a new .jsfl (Flash JavaScript) file and enter the following code:

var sayHello;
sayHello = 'Hello!';

Save this file in the Flash Configuration folder. I like to create a new folder for ‘JSFL Includes’. Save this file as ‘external_file.jsfl’.

Build another JSFL file that runs the previous file and traces the value created in it to the output panel:

// the Flash “equivalent” of a #include command.
fl.runScript(fl.configURI + 'JSFL Includes/external_file.jsfl');

// trace the value in our external file.
fl.trace(sayHello);

Save this file in the Flash Configuration/Commands folder as, ‘Test – External File.jsfl’. That way, we can run this script from any Flash document from the Commands menu. Open a new .fla document (*) and run the command (**). This produces the output: ‘Hello!’ We have successfully accessed the variable created in our external file and output it to the screen.

Flash caches JSFL values in its memory until the Flash IDE (application) is closed. This means that if we have run a JSFL file that creates a variable, we can access it in any JSFL file that is run after that. For example, without closing Flash since our previous test, let’s comment out the line in our ‘Test – External File.jsfl’ that runs the external file. Flash should still have the contents of the external file in memory.

//fl.runScript(fl.configURI + 'JSFL Includes/external_file.jsfl');
fl.trace(sayHello);
Save the file and run it from the Commands menu. It will still produce the output: ‘Hello!’ This means that the variable, sayHello is still in memory from the last time the external file was run.


Creating a Utils Object in a JSFL file that can be used like a registered namespace (class) in other JSFL files:

We will first make a jsfl file that creates a Utils Object. Create a new .jsfl (Flash JavaScript) file and enter the following code:

var Utils = new Object();
var sayHelloFromUtils;

Utils.init = function()
{
sayHelloFromUtils = 'Hello from Utils!';
};

/*
@Return the value of sayHelloFromUtils.
*/
Utils.getResponse = function()
{
return sayHelloFromUtils;
}

/*
This is an example of how the Utils Object can be used to perform common tasks.
@Returns a reference object to the current document DOM if a document is open.
*/
Utils.getCurrentDocument = function()
{
try
{
var doc;
doc = fl.getDocumentDOM();
if(doc == undefined)
throw 'Error';
return doc;
}
catch(error)
{
if(error == 'Error')
{
fl.trace('ERROR: failed to get current document');
}
}
};

Utils.init();

Save the file in the Flash Configuration folder\JSFL\Includes, with filename, ‘utils.jsfl’.

Now we will create another .jsfl file that will get a response from the Utils Object. Create a new .jsfl (Flash JavaScript) file and enter the following code:

fl.trace('JSFL - Get a response from the cached Utils object: '+ Utils.getResponse());

Save the file in the Flash Configuration folder\JSFL, with filename, ‘test_external_file.jsfl’;

Now, create a panel that will run the JSFL. Create a new .fla document (*). In Modify > Document, set the document width to 200 px and height to 50 px.

On the first frame, enter the following code:

import mx.controls.Button;
import mx.utils.Delegate;

// Run external JSFL file that we want to keep in Flash's memory.
MMExecute("fl.runScript(fl.configURI + 'JSFL/Includes/utils.jsfl');");
trace("Flash - Ran external JSFL file that created variable, sayHello.");

// Create a button on the stage.
this.createClassObject(mx.controls.Button, "btnRunJSFL", 10, {label:"Run JSFL"});
btnRunJSFL.move(47.35, 15.35);

// Create an onClick event handler for the button.
function btnRunJSFL_onClick(eventObj:Object) : Void
{
MMExecute("fl.runScript(fl.configURI + 'JSFL/test_external_file.jsfl');");
trace("Flash - Ran a test JSFL file that tries to access the cached Utils object.");
};
btnRunJSFL.addEventListener("click", Delegate.create(this, btnRunJSFL_onClick));

stop();

This code runs our first JSFL file that creates a Utils Object, which gets stored in Flash’s memory cache. We also create a button on the stage that, when clicked, runs a JSFL file that will access the Utils Object as if it is a registered namespace.

Save the document in the Flash Configuration folder > WindowsSWF, with the filename, ‘Test – External JSFL File.fla’ and then publish the swf file (* Publish Settings: Flash 8, Actionscript 2.0) so that the swf is in the WindowsSWF folder.

Close and then re-open Flash. Create a new .fla document then go to Window > Other Panels > Test – External JSFL File, to open our custom panel. Click the ‘Run JSFL’ button to see the results (**).