F9 for actions

Some possibly useful and definitely unuseful ActionScript 3.0 code.

Posts tagged JSFL

Aug 31

Moving Library Assets from FLA to FLA with JSFL

One of my latest projects had me scratching my head while I was doing my R&D. The project was to build a Flash panel that could pull assets in from another FLA that acts as a library. My first thought was to see if there was any way to copy items directly out of the library and add or paste them into the current working FLA. In my research this just isn’t possible, but I was still able to get the panel built.

Basically the process that works is as follows:

  1. Open the library FLA file
  2. Put the wanted library item on the stage with library.addItemToDocument()
  3. Use fl.getDocumentDOM().clipCut() to add the item to the clipboard
  4. Close the library
  5. Use fl.getDocumentDOM().clipPaste() to add the asset to the FLA

It seems pretty simple and it is, truly the hardest part was figuring out that that was the process to use. Here’s what my JSFL looks like:

//Loads an asset from the libraryFile into the current working FLA
//libraryFile - the filename of the library (icons.fla)
//AssetName - the asset name as it is in the FLA library
function LoadAsset( libraryFile, AssetName ) {
	
	var originalDoc = fl.getDocumentDOM();
	
	fl.openDocument( librariesPath() + libraryFile );
	var libDoc = fl.getDocumentDOM();
	
	fl.getDocumentDOM().library.addItemToDocument({x:0,y:0}, AssetName);
	
	libDoc.clipCut();
	fl.closeDocument(fl.getDocumentDOM(), false);
	originalDoc.clipPaste();
}

//this is where I'm keeping the library FLA files
function librariesPath() {
	return fl.configURI + 'Resources/AssetLibraries/';
}

And here’s the panel:

The way it all works is the panel loads an XML file that defines all the library files and populates the combo box. Then when a file is selected from the combo box, I have another JSFL function that opens the FLA and grabs all the top-level (not in folders) asset names from the library and returns them back to the panel as a comma delimited string. The string is broken up into an array and populates the asset list. The ‘Add to Stage’ button then uses the name in the list to call back to the JSFL referenced above.

Hope this helps anyone looking to do this, I found no help on this via Google in my own research.


May 10

Converting a JSFL Object to JSON

I couldn’t find any online resource to talk about how people are passing data from JSFL scripts to their Flash Panels. When you use MMExecute you can return a String value back to Flash, or if you’re making a function available via ExternalInterface you can also pass primitive datatypes into the functions. However when you start talking about passing Objects of any significant structure, things get a little hairy.

So it was my conclusion that I needed to serialize my data object in JSFL in order to pass it to Flash. And when you talk serialize in Javascript you should think JSON, so I set in search for an out-of-the-box Javascript Object to JSON serializing function - should be easy to find right? Unfortunately, after two days of struggling with different implementations, I found that some of the String functions like match and replace cause errors (not saying they aren’t implemented, just for whatever reason they didn’t work for me like they do in regular JS).

So without further delay, here is my very less-than perfect, less-than optimized JSFL Object to JSON String functions. (Also note that these will convert any Arrays nested in your Objects to Objects themselves with keys being the string equivalent of their indexes.

— UPDATE —

I wrote this post before testing it completely against sending the JSON string over to Flash via the SWFPanel.call() method. What I found out while trying to do this is that you can not, for whatever reason, send literal single or double quotes embedded within the string variable. So I changed up my function to use tildes (~) instead of quotes, which I then replaced with quotes over on the AS3 side of things.

function serializeObject( o ) {
	var s = '{';
	s += so( o );
	//remove the last comma
	s = s.slice( 0, s.length-1 );
	s += '}';

	return s;
}

function so( o ) {
	var s = '';

	for( var key in o ) {
		if( o[key].toString() == '[object Parameter]' ) {
			s +=  '~' + key + '~:{';
			s += so( o[key] );

			//remove the last comma
			s = s.slice( 0, s.length-1 );
			s += '},';
		} else if(o[key].constructor === Array ) {

			for( var key2 in o[key] ) {
				if(key2 != "" && o[key][key2].value != '""') {
					s += '~' + key2 + '~:~' + o[key][key2].value + '~,';
				}
			}

		}else {
			if(key != "" && o[key] != '""') {
				s += '~' + key + '~:~' + o[key].toString()  + '~,';
			}
		}
	}

	return s;
}

And here is what I do on the AS3 side to convert it into an Object from JSON (note I’m using the as3corelib to JSON decode the string).

//function exposed via ExternalInterface and called from JSFL
public function setParameters( s:String ):void {

	var p:RegExp = /~/g;
	var t:String = s.replace(p, '"');		

	var o:Object = JSON.decode(t);

}

Like I said it’s not a perfect implementation, but it works pretty well for nested Objects. To put this in perspective, I’m using this so I can pass all the attributes of multiple selected components on the stage (via the fl.getDocumentDOM().selection Array) to my WindowSWF Panels which will allow me to change and configure the components’ properties.


May 5

Updating (redrawing) Components from JSFL

I am building a Flash Panel for a Custom Component I have built. I didn’t want to use the CustomUI setting for components because I wanted to be able to allow this panel to edit multiple instances of this component at once. The problem I have run into is figuring out how to get my component instances on the stage to update or redraw themselves when a property changes.

I searched and searched for a way to do this and didn’t find anything that worked. The problem lies in the fact that the Flash Panel doesn’t (and shouldn’t in my opinion) know about the specific instances on the stage. JSFL does know about those instances but only as it’s predefined Element object type, so there is no way to access public functions of that component from JSFL. You can access the Inspectable properties of the component, but changing these values does not tell Flash’s live preview to redraw the components.

So, if you were able to follow all of that, the problem here is that I can update properties of my components using my Flash Panel and JSFL, but there is no way to force redraw those components on the stage when I change the properties… until now.

I found that when deselecting my components that I had just changed properties on, the stage would redraw them. So all I did in JSFL was deselect the components and reselect them. Thankfully this forces a redraw of the live preview components. Here’s what that JSFL code looks like:

function invalidateStage() {

	var selectedArray = fl.getDocumentDOM().selection;
	fl.getDocumentDOM().selectNone();
	fl.getDocumentDOM().selection = selectedArray;

}

Now when I call invalidateStage in my JSFL after I have changed a property on my component, the stage will effortlessly update before my eyes.