AS3Eval library is complaining about "Variable compile is not defined" - actionscript-3

I'm working with FlashBuilder 4.6 and using AS3Eval v0.3 library from: http://eval.hurlant.com/
The library is fully working in one of my Flex 4.6.0 projects but not in the other (same library linkage "Merge into code" of EvalES4.swc library).
In the first project following code works:
private var compiler:CompiledESC = new CompiledESC;
public function compile(code: String) : ByteArray {
return compiler.eval(code);
}
In the other project, it fails with:
ReferenceError: Error #1065: Variable compile is not defined.
The error is referring to following line in the AS3Eval library (that EvalES4.swc file)
var compile:Function = getDefinitionByName("ESC::compile") as Function;
Looks like the library has problem to fully load Tamarin ESC in the other project.
I've checked that both projects (they are using the same library... but still checked) load the Tamarin ESC successfully via
// inside CompiledESC.as
private function loadESC():void {
var a:Array = [
new debug_abc as ByteArray,
new util_abc as ByteArray,
new bytes_tamarin_abc as ByteArray,
new util_tamarin_abc as ByteArray,
new lex_char_abc as ByteArray,
new lex_scan_abc as ByteArray,
new lex_token_abc as ByteArray,
new ast_abc as ByteArray,
new parse_abc as ByteArray,
new asm_abc as ByteArray,
new abc_abc as ByteArray,
new emit_abc as ByteArray,
new cogen_abc as ByteArray,
new cogen_stmt_abc as ByteArray,
new cogen_expr_abc as ByteArray,
new esc_core_abc as ByteArray,
new eval_support_abc as ByteArray,
new esc_env_abc as ByteArray,
]
ByteLoader.loadBytes(a, true);
}
These ByteArray classes are embedded inside the SWC using [Embed] (looking through the library code) and all ByteArrays are in both cases initialized and loaded via ByteLoader.
So far, I have not found any clue why compiling is working for my first project but not for the other. Does anybody has similar experiences or some hints what may cause the problem?
Best,
Jakub

Ha!
I was too desperate, solution found.
It appears, that CompiledESC cannot be used in the same frame where it was created.
So in the other words, something like this won't work:
public function compile(code: String) : ByteArray {
var compiler:CompiledESC = new CompiledESC();
return compiler.eval(code);
}
But following code will work (application loads, initializes CompileESC and then user clicks the RUN! button, which happens in different frame from the one where CompileESC was created):
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600"
>
<fx:Script>
<![CDATA[
import com.hurlant.eval.CompiledESC;
import mx.events.FlexEvent;
private var compiler:CompiledESC = new CompiledESC();
protected function button1_clickHandler(event:MouseEvent):void
{
compiler.eval("trace(\"hello!\")");
}
]]>
</fx:Script>
<mx:Button label="RUN!" click="button1_clickHandler(event)" />
</s:Application>
I do not know exactly why, but I suspect it has something to do with the way Flash is loading its bytecode. When you try to insert some code into VM and use it in the same frame you're asking for troubles because your code has just not been loaded yet. You have to "yield" the thread and wait for another frame, somewhere in-between Tamarin ESC code gets loaded.
Note that the same applies for compiled code being loaded via ByteBuffer by you! Whenever you do something like
ByteLoader.load(compiler.eval(myAS3Code));
Do not expect that myAS3Code will be executed right away. Again, the code will be loaded whenever you "yield" the thread and let VM to truly load your code.
Hope this helps someone in the same situation.
Best,
Jakub

You should check if the permissions are the same of first project. You're merging (if I understood right) a library from outside, is this allowed?

Related

load unknown number of external images without XML

I need to know how to load unknown number of images from external folder without using XML
help me please,
thank you
So from your comments I'm assuming this is an AIR Application, so you can access the filesystem via File class.
First of all, you need to get a File object that points to your folder, the easiest way is to hardcode it. A slightly more complex approach would involve opening a dialog, where the user can select his desired folder (using File.browseForOpen).
Let's take the easy route and define a constant path to the folder, here it's a folder called "images" in the users documents folder:
File imageFolder = File.documentsDirectory.resolvePath("images");
Once we have a folder instance, we can use the getDirectoryListing method to list all files within that folder. Here's an example:
// create a vector that will contain all our images
var images:Vector.<File> = new Vector.<File>();
// first, check if that folder really exists
if(imageFolder.exists && imageFolder.isDirectory){
var files:Array = imageFolder.getDirectoryListing();
for each(var file:File in files){
// match the filename against a pattern, here only files
// that end in jpg, jpeg, png or gif will be accepted
if(file.name.match(/\.(jpe?g|png|gif)$/i)){
images.push(file);
}
}
}
// at this point, the images vector will contain all image files
// that were found in the folder, or nothing if no images were found
// or the folder didn't exist.
To load the files into your application, you can do something like this:
for each(var file:File in images){
// use a FileStream to read the file data into a ByteArray
var stream:FileStream = new FileStream();
stream.open(file, FileMode.READ);
var bytes:ByteArray = new ByteArray();
stream.readBytes(bytes);
stream.close();
// create a loader and load the image into it
var loader:Loader = new Loader();
// use the loadBytes method to read the data
loader.loadBytes(bytes);
// you can add the loader to the scene, so that it will be visible.
// These loaders will all be at 0, 0 coordinates, so maybe change
// the x and y coordinates to something more meaningful/useful.
this.addChild(loader);
}
Can you elaborate a little bit better the question? If it's a user action (i.e. the user needs to upload some photos) I'd use the File API - you can see examples here - otherwise, if it's from the server side I'd use a PHP or Phyton script.
Assuming that your application is Air (Desktop application), this code will be useful:
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init()">
<mx:Script>
<![CDATA[
public function init():void{
var fs:FileStream = new FileStream();
var f:File = new File("c:/imgTest.jpg");
var b:ByteArray = new ByteArray();
fs.open(f, FileMode.READ);
fs.readBytes(b,0,fs.bytesAvailable);
idImg.source = b;
fs.close();
}
]]>
</mx:Script>
<mx:Image id="idImg" width="100%" height="100%"/>
</mx:WindowedApplication>
Place an image in c:/imgTest.jpg. Note that this image is outside from your project path.
You have other options to load images, but these must be accessible by URL, or should be in the path of the project.
Here a links that will be usefull for load images in Flex Air and Web:
Working with File objects in AIR
Image as ByteArray in Flex
Image Control
Programatically load images in Flex.
Note: i tried only with JPG files, i do not know if this works with other types.

How to access variables of the 'main' SWF from a loaded SWF by SWFLoader?

I'm working on a website built in Flex, but the SWF file after compiling is very large. The websites has multiple pages.
So I thought to create a new MXML project for every new page and load it with SWFLoader.
I've found this example:
public function extSwfLoaded(evt:Event):void {
var sysmgr:SystemManager = (extSwf.content as SystemManager);
sysmgr.addEventListener(FlexEvent.APPLICATION_COMPLETE, function(event:FlexEvent):void {
var sysmgr:SystemManager = (event.currentTarget as SystemManager);
var swfApp:Application = (sysmgr.application as Application);
}
});
public function gotoPage(page:String):void {
extSwf.addEventListener(Event.INIT, extSwfLoaded);
var now:Date = new Date();
switch(page) {
case "register":
openedPage = "register";
extSwf.load('modules/register.swf?anticache=' + now.getTime());
break;
}
}
And in the MXML:
<mx:SWFLoader id="extSwf" complete="extSwfLoaded(event)" width="100%" />
<s:Label text="Register" useHandCursor="true" buttonMode="true" click="gotoPage('register')" />
This works perfect. The content of modules/register.swf is showed at the place of the extSwf SWFLoader.
But: I've no idea how to interact with the 'main' SWF and the loaded SWF file.
I have some global variables that I want to send to every loaded SWF file
(and some variables that I want to send from the loaded SWF file to the 'main' SWF file).
A website said that I can send these variables by loading modules/register.swf?var1=hi&var2=hello, but someone who can see the HTTP headers (for example with Live HTTP Headers in Firefox) can see all these variables.
So, is it possible to load a SWF file and send them some variables? And when I have a new global variable, I don't have to open and edit and recompile every MXML project?
Thank you very much!
It looks like this can show the variables of my 'main' MXML.
import mx.controls.Alert;
import mx.core.FlexGlobals;
Alert.show(FlexGlobals.topLevelApplication.myvar);
And for using public functions:
FlexGlobals.topLevelApplication.myFunction();
I just needed the right keywords (top level application) to search for it on the internet.

How to use external actionscript classes in FlashBuilder (thought I knew)

I'm trying to implement CSVLib in an Air application and am getting an error that seems wholly illogical to me.
"1120: Access of undefined property csv."
and
"1120: Access of undefined property completeHandler."
The only thing I can think is it's not importing the csv class properly, or the class itself is broken somehow? I know my import path is correct because I typed it out directly based on automatic hinting. The code below is copied directly from the how-to wiki on the csv lib site.
Or is there something special you need to do to get external actionscript classes to work in flashbuilder?
<fx:Script>
<![CDATA[
import com.shortybmc.*;
import com.shortybmc.data.parser.CSV;
var csv:CSV = new CSV();
csv.addEventListener (Event.COMPLETE, completeHandler);
csv.load (new URLRequest('example-2.csv'));
function completeHandler (event: Event)
{
trace ( csv.data.join('\r') );
// do something ...
}
]]>
</fx:Script>
In this case, the problem is somewhere else. The fx:Script tag is within a MXML file, which represents a class definition.
Your error happens, because you have code within the class definition (i.e. outside of a method). You can write this instead for example:
<fx:Script>
<![CDATA[
import com.shortybmc.*;
import com.shortybmc.data.parser.CSV;
private var csv:CSV;
private function init ():void
{
csv = new CSV();
csv.addEventListener (Event.COMPLETE, completeHandler);
csv.load (new URLRequest('example-2.csv'));
}
private function completeHandler (event: Event):void
{
trace ( csv.data.join('\r') );
// do something ...
}
]]>
</fx:Script>
Then you need to make sure that the init method is actually called; you can do this in the complete handler of your MXML object.

How to run an external SWF inside a Flex Application?

EDIT: Due to the answer I change the code posted. I've added the Security.allowDomain("*") line and that line throws me an error. So, how can that be made?
I want to run an Action Script 3.0 Application into a Flex Application. To do this I've done the following:
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication windowComplete="loadSwfApplication()" xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
private function loadSwfApplication()
{
// The next line throws me an error.
Security.allowDomain("*");
var urlRequest:URLRequest = new URLRequest("path/to/the/application.swf");
swfLoader.addEventListener(Event.COMPLETE, loadComplete);
swfLoader.load(urlRequest);
}
private function loadComplete(completeEvent:Event)
{
var swfApplication:* = completeEvent.target.content;
swfApplication.init(); // this is a Function that I made it in the Root class of swfApplication
}
]]>
</mx:Script>
<mx:SWFLoader id="sfwLoader"/>
</mx:WindowedApplication>
The problem is that in the calling of swfApplication.init(); the AIR Player throws me an exception:
Security sandbox violation: caller file:///path/to/the/application.swf cannot access Stage owned by app:/SWFApplicationLoader.swf.
This is because somewhere in application.swf I use the stage like this:
if (root.stage != null)
root.stage.addEventListener(Event.REMOVED, someFunction);
root.stage.stageFocusRect = false;
How can I load this swf application and USE the stage without any problems?
You can try to load your SWF temporarily into a ByteArray and then load it with your SWFLoader.
Don't forget to set allowCodeImport to true since your SWF has as code inside.
Of course be sure that your loaded swf is secure enough for your application since it will have access at all your property.
private function loadSwfApplication():void {
// load the file with URLLoader into a bytearray
var loader:URLLoader=new URLLoader();
// binary format since it a SWF
loader.dataFormat=URLLoaderDataFormat.BINARY;
loader.addEventListener(Event.COMPLETE, onSWFLoaded);
//load the file
loader.load(new URLRequest("path/to/the/application.swf"));
}
private function onSWFLoaded(e:Event):void {
// remove the event
var loader:URLLoader=URLLoader(e.target);
loader.removeEventListener(Event.COMPLETE, onSWFLoaded);
// add an Application context and allow bytecode execution
var context:LoaderContext=new LoaderContext();
context.allowCodeImport=true;
// set the new context on SWFLoader
sfwLoader.loaderContext = context;
sfwLoader.addEventListener(Event.COMPLETE, loadComplete);
// load the data from the bytearray
sfwLoader.load(loader.data);
}
// your load complete function
private function loadComplete(completeEvent:Event):void {
var swfApplication:* = completeEvent.target.content;
swfApplication.init(); // this is a Function that I made it in the Root
// class of swfApplication
}
If they are being loaded from different domains you are going to have to add a security exception - http://livedocs.adobe.com/flex/3/html/help.html?content=05B_Security_08.html
if its being run locally youre probably going to have to add them to the list of trusted files or folders in the settings manager - http://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager04.html#117502
Assuming that the external SWF is also in the application directory, you could try loading it using the app:/ scheme:
var urlRequest:URLRequest = new URLRequest("app:/path/application.swf");
That may put it into the same security context as the main application.
One thing you may want to consider is that if you are trying to run a SWF from inside your application directory in AIR, AIR restricts execution of files. If you copy the file to a temp file and the run it (along with allowLoadBytesCodeExecution set to true) then it works.
var file:File = File.applicationDirectory.resolvePath("myFile.swf");
this.tmpFile = File.createTempDirectory().resolvePath("myFile.swf");
file.copyTo(this.tmpFile);
imagePreview.loaderContext = lc;
imagePreview.source = tmpFile.url;
it won't work for Flex Projectors.
Only we use SWFLoader and LocalConnection because they can communicate between external swf and main swf. Thanks for support!
Can you read my tutorial from Adobe's Forum
It is very better than MovieClip or Object-Callers
Thank for resolved solution :)
Best regards, Jens

Saving XML file in AS3 is possible

var xml:XML = <myXml>
<item prop="1" />
<item prop="2" />
</myXml>;
I need to save as xml file in local harddisk(project directory).
Is it possible to save in as3 itself?
I threw this together, and sure enough you can save to .XML using the following as a minimalist example.
package com.hodgedev.xmlcreator
{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.utils.ByteArray;
import flash.net.FileReference;
/**
* ...
* #author Brian Hodge (brian#hodgedev.com)
*/
public class Main extends Sprite
{
private var _xml:XML;
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
//Calling the save method requires user interaction and Flash Player 10
stage.addEventListener(MouseEvent.MOUSE_DOWN, _onMouseDown);
_xml= <xml>
<test>data</test>
</xml>;
}
private function _onMouseDown(e:MouseEvent):void
{
var ba:ByteArray = new ByteArray();
ba.writeUTFBytes(_xml);
//ba.
var fr:FileReference = new FileReference();
fr.addEventListener(Event.SELECT, _onRefSelect);
fr.addEventListener(Event.CANCEL, _onRefCancel);
fr.save(ba, "filename.xml");
}
private function _onRefSelect(e:Event):void
{
trace('select');
}
private function _onRefCancel(e:Event):void
{
trace('cancel');
}
}
}
There are some things to note.
You require Flash Player 10 to use the save method of the FileReference class.
In order to do anything that INVOKES a prompt, Flash requires user interaction like keyboard or mouse input.
In the above I listen for MouseEvent.MOUSE_DOWN on the stage to serve as the USER INTERACTION which is required to invoke the save prompt.
I setup a basic XML structure within the code (this will typically come from and external source and will work fine both ways.
A ByteArray is created and the XML is written to the ByteArray.
The save method of the FileReference class requires a ByteArray and default save name be passed as the two parameters.
I hope this helps.
if you want to store it locally (on the client PC) , you can use a local shared object. Refer to this tutorial
I'm sorry, your question isn't very clear.
Are you asking if you can save a file to the hard drive from within a compile SWF written in AS3?
Or are you asking if you can include a raw XML file in your AS3 project without needing to write it out as a variable?
If you meant the former, no -- not without Adobe AIR. You can save data locally as a SharedObject, but not as an arbitrary file in the file system.
If the latter, then yes -- you must embed the file just as you would embed another resource (such as an image or a sound). However, it looks like there might be a bug in Flash that makes this non-trivial to figure out how to do.
This link might be of help to you.
[Embed(source='../../../../assets/levels/test.xml', mimeType="application/octet-stream")]
public static const Level_Test:Class;
And then to parse the XML:
var ba:ByteArray = (new Levels.Level_Test()) as ByteArray;
var s:String = ba.readUTFBytes( ba.length );
xml = new XML( s );
Apologies if neither of those questions are what you were actually asking.
Cheers!