How many levels deep can you nest swf's - will 3 work - like child inside parent inside grandparent? - actionscript-3

The code at the bottom of this post nests an AS2 "player" inside a Flex app (AS3). Look at the part that says "[AS2 player swf]". That AS2 player swf has almost no code - just one function that loads an asset from a remote server. Very simple:
this.onLoad = function(){
content.loadMovie("http://URL_TO_REMOTE_SWF");
};
And it has no other assets. Just one MovieClip - "content".
So we've got a Flex app that loads an AS2 swf that loads a remote swf. No dice. Remote swf doesn't load and display. No errors reported.
Important: The AS2 app on its own works. If I go navigate to it in my file system and double click, remote swf does indeed load and display. So I've established that the unwrapped AS2 swf player works and does indeed load remote swf's. Something about nesting that player inside the Flex app creates a problem. Are you not allowed to do this - 3 levels of swf nesting (child - parent - grandparent)?
UPDATE: Target remote swf's load and display fine directly in Flex as well (skipping AS2 swf). So problem doesn't appear to be with AVM1/AVM2 discrepancy between target content and Flex app. Could be AVM1/AVM2 problem with how the Flex app displays the AS2 swf but only when it contains content.loadMovie("http://URL_TO_REMOTE_SWF"). Loads and displays without that line.
UPDATE: This is a Flex packaged IOS application and thus is subject to ActionScript restrictions for dynamically loaded code. But it doesn't work in a regular Flex (non packaged IOS) app either which is not subject to these limitations. THE FOLLOWING CODE SHOULD WORK AS FAR AS I CAN TELL IN A REGULAR FLEX BROWSER BASED APPLICATION.
<?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"
initialize="init()">
<fx:Script>
<![CDATA[
import mx.core.UIComponent;
private var request:URLRequest = new URLRequest("[AS2 player swf]");
private var loader:Loader = new Loader();
private var myComponent:UIComponent = new UIComponent;
private function init():void{
myComponent.percentHeight = 100;
myComponent.percentWidth = 100;
loader.load(request);
myComponent.addChild(loader);
player.addElement(myComponent);
}
]]>
</fx:Script>
<s:Group id="player"
height="100%" width="100%"/>
</s:Application>

Since all Sprites and MovieClips are both display objects and display object containers, you could theoretically keep nesting them forever. There are physical restraints, of course, but I see no reason why there should be a limit other than memory and processing power (at some point, refreshing the screen will become sluggish, and eventually painful to wait for).
I've searched the web to see if I could find any information about a built-in limit enforced by the AVM, any documents or error codes that would suggest there is one, but I couldn't find anything at all. Of course, that doesn't mean there isn't - it just means the limit hasn't been reached by many people yet ;)
So if anyone else knows more...
EDIT
Since the information about your loaded MovieClip is new, here's my update: AVM1 (AS1 & 2) SWFs don't always execute in the same way when loaded into an AVM2 (AS3) movie. This has nothing to do with the number of nested display objects. I originally thought this was because the _lockroot property was removed in AS3, but I've searched a bit and found out it is assumed true by default, and should therefore not be a problem.
There may be many things about your scenario that are different from when the file runs on its own, and I would start by looking at these:
Do you use relative path names to load other data into the AS2 swf?
Do you get any error messages (particularly, security sandbox violation errors)?
Does your AS2 SWF rely on external variables to be set (FlashVars)
Does your AS2 SWF need ExternalInterface access?
FINAL EDIT
Since this new info has just come up:
There is absolutely no way to execute dynamically loaded AVM byte code on an iPad at runtime, for the same reason that there is no Java VM and no Flash Player for iOS: Apple does not allow the execution of dynamically loaded byte code at all. Period.
Hence, you can load and play SWF animations, but not ActionScript. It will simply be ignored. Also, neither of the apps that you did run is in fact AVM byte code - they are compiled into LLVM byte code instead.
You will have to find a way to check for the end of the animation that does not involve stop() commands. Perhaps this can be done by checking frame numbers from your AS3 container?

To answer you question directly, yes, you can load AS2 swfs that way. And you code looks correct to me.
I suspect it is loading the AS2 swf fine, but that the AS2 swf is not initializing correctly for some reason. Perhaps it is a video player that has no visible assets besides the loaded video. AS2 tends to fail without throwing errors, which makes it hard to debug. I would suggest publishing an AS2 swf with some graphics in it but with no code that could fail. No code at all. Maybe just a timeline animation of a moving circle or something, then load that. In that way you can eliminate your Flex code as the source of the problem.
If it turns out that your test movie works, then you will need to determine what resource the loaded swf is expecting that it is not receiving.

Related

How to properly add "Replay" functionality to a self-preloaded Flash movie?

I made a Flash project in FlashDevelop to create an Ad.
The Preloader is setup by making use of the Additional Compiler argument:
-frame=NameOfLabel,NameOfMainClass
My main class is simply called "Main", at the top / default package level.
So frame #1, being the Preloader portion of the SWF, has:
Very few bitmaps, vector-graphics and text (to stay under 50kb);
A YouTube video player in the center (does not count in the filesize limit);
The frame #2 has everything else (the Main class basically embeds all it's dependencies). This includes:
Assets from precompiled SWF (Bitmaps, Symbols, Fonts, XML data);
All classes imported (this is recursive for every classes importing other classes);
Now my big problem is, my client requested the "replay" functionality long after I've completed 99.9% of the project.
I have the project more-or-less broken into different states (Intro, Ready, SlideMenu, etc.), but I'm not sure how I can easily reset the Flash movie back to the very beginning (where it was preloading and showing the YouTube video).
The easy solution would be to simply call an ExternalInterface JavaScript method that would refresh the Flash container, BUT I don't think I have control over what's going on the HTML / JavaScript side.
Is there an easy way to invoke a replay function from AS3?
Would not simply going back to frame 1 do the trick ?
The following seems to do the trick!
private function onReplayClick(e:MouseEvent):void {
var theStage:Stage = this.stage; //Temporarly store the stage.
//Kill any animations happening:
TweenMax.killAll(); //3rd party, may not be applicable for you :P
//Remove ALL containers / child DisplayObjects
SpriteUtils.recursiveRemove(theStage); //custom-made
// (this object is no longer attached to the stage at this point)
//Nullify any Singleton / Static variables:
Main.INST = null;
// Load the 'bytes' of the current SWF in a new Loader, then...
// add it to the stage
var swf:Loader = new Loader();
swf.loadBytes( theStage.loaderInfo.bytes );
theStage.addChild( swf );
}
By doing a deep recursive cleanup of the DisplayObjects, and any static variables (like Singleton instances), it leaves you with a blank stage.
After that, you can instantiate a new Loader that will load the SWF itself via the current LoaderInfo's bytes property.
Add the Loader to the Stage, and you're up and running again!

Never Have Access to Embedded SWF Content Through Loader

I am working on embedding a SWF that is just a converted PDF created with InDesign. The SWF is 15 pages, 1 page per frame. It does include TLF text and there is no way around that unless the document is completely recreated in Flash. The problem is that when I load the SWF into my application, I never have access to the root MovieClip.
this.loader = new Loader();
this.loader.contentLoaderInfo.addEventListener( Event.INIT, this.loaderInitHandler );
this.loader.contentLoaderInfo.addEventListener( Event.COMPLETE, this.loaderInitHandler );
this.loader.load( new URLRequest( "assets/doc.swf" ) );
When I look at the loader after both the INIT and COMPLETE events as well as later on by stopping the app in debug mode, I do not see a MovieClip. Instead, the loader.content is of type doc_fla.MainTimeline__Preloader__ which contains two children, an empty Shape and another Loader. That loader, when checking INIT and COMPLETE events results in the exact same situation and it appears to be a never ending line of Loaders whose contents are "Preloaders".
Could anyone shed some light as to what is causing this or how I might fix it? I've poured a good 2 hours into solving this and I am no closer to a solution than when I started.
ActionScript Mobile project
Compiling using AIR 3.6
Testing in emulator as well as on iPad and on Nexus 7
I recompiled the SWF and embedded the TLF libraries (under ActionScript settings, just set the TLF swc which was already there to be embedded) in it and this seems to have fixed the issue. I swear I had tried that before to no success, but it worked this time.

Internal AS3 preloader & stage issues

I need to create a single SWF with no external files, so I'm trying to add an internal preloader to my Flash project which has [embed] assets. I know [embed] causes problems with preloaders because it puts the assets on frame 1. I have tried the solutions recommended in these posts, where you set the document class to your preloader class:
Preloader for SWF with embed bytearray
How to create Preloader in AS3
I can get it to work, but ONLY if I comment out any lines of code that involve the stage, otherwise I get an "Error #1009: Cannot access a property or method of a null object reference." Those lines are essential though, so does anyone know how to fix those errors with the stage?
You haven't posted your code or your fla, so all I can do is share what works for me.
First, I wouldn't use Embed. Instead, use a swc. I have found that Embed can be unreliable as far as actually getting the entire asset in there (at least when publishing with Flash Builder + Flash Pro, which is my workflow).
Once you have your assets in a swc, try the following steps:
Set your export frame to Frame 10 (or any frame other than 1--I like frame 10 because then you can read the label that says "Preloader")
Put your actual content on frame 11. You can structure this a lot of ways. Since I program to Interfaces, I give whatever is on frame 11 an instance name and then use a setter to determine that my "first thing" has been placed on stage. I can get away with this because my main Document Class just knows the definition of the Interface, not the full implementation of the Class, so the Class does not need to load for the main Document Class to work. You probably aren't truly using the timeline and probably didn't program to interfaces, so you'll probably just set the base class of the symbol that's on frame 11 to the main logic of whatever you're trying to do.
Put your preloader graphics in Frame 1. I'm not sure why your stage references are so important. I, personally, don't use any logic in the preloader. Instead, I use a spinner that spans frames 1-10 (plus the word "Loading...". The spinner just spins while the classes load. The embed frame acts as a temporary "stop" that just holds the timeline back until those classes have been loaded. Once the classes have been loaded, the timeline will act like you called play() on it. So it really can be that simple. If you need it to be more complicated, give one of your preloader graphics an instance name and set up a getter/setter pair for it, then use the setter to trigger your logic that accesses the stage. You are pretty much guaranteed to have a valid stage at that point.
Word of warning: if you did make use of the timelime, you will get strange results if you try to jump to a frame that isn't loaded yet, so make sure to check to see if a given frame is loaded if it's near the end of your main timeline and your main timeline is heavy with assets before calling goToAndPlay() or goToAndStop().
Some references that might help you further:
Preloaders vs as 3 (I'd recommend you read the entire series this is part of. This is an amazing series I wish I'd found 3 years ago)
Solving the Frame 2 Problem Presentation and code
Combining the Timeline with OOP The example code for that is here (long story)

Loading and using SWF files

I'm new to AS3, and am trying to understand how externally loaded SWFs work in AS3. Since Flash 4/5, it was common to have one main SWF file in a Flash web project, and then load other SWF files into it, often for various "sections" of a website or web project. In the main file, we'd have masks animating the container movieclip(in which external sections/SWF files were loaded) and have animations and transitions play as the section finished loading and the loaded content was displayed.
In AS3, I've used the Loader class to load and display the external file, my main problem is in communicating with the loaded content, call it's functions, or call root functions from it.
In AS2, we could use someMovieClip.loadMovie("ExternalContent.swf") and the ExternalContent file would load inside someMovieClip. You could access functions on the "External.swf" main timeline using someMovieClip.function();. And inside the "ExternalContent.swf", we could use _root.function() to access functions in the main file ExternalContent was being loaded into. Doing this in AS3 seems bizarre and neurotic, and I feel like I'm missing something fairly basic here.
//Loading in ExternalContent.swf into the sprite
//ExternalContent has a movieclip called "boxes" on it's main timeline
//boxes has a boxesPrompt() function in it's timeline.
var sprite:Sprite = new Sprite();
addChild(sprite);
var loader:Loader = new Loader();
loader.load(new URLRequest("ExternalContent.swf"));
loader.contentLoaderInfo.addEventListener(Event.COMPLETE,onLoaded);
function onLoaded(event:Event):void
{
sprite.addChild(event.target.content);
sprite.boxes.boxesPrompt();
//Flash gives the following compiler error at the above
//Scene 1, Layer 'Layer 1', Frame 1, Line 21 1119: Access of possibly undefined property boxes through a reference with static type flash.display:Sprite.
//But when I comment out sprite.boxes.boxesPrompt() and use this, it works:
event.target.content.boxes.boxesPrompt()
}
The boxesPrompt() function inside the "ExternalContent.swf" just traces it's parent, grand-parent, and great grand-parent - trace(this.parent.parent.parent);. And when I call that function inside the onLoaded event-handler using "event.target.content.boxes.boxesPrompt()", it shows that the Boxes object(which was on the main timeline of External.SWF), has a parent movieclip, a grand-parent sprite, and a great grand-parent object mainTimeline.
I thought re-parenting the loaded content into the sprite would allow me to access the loaded content as easily as loadMovie() used to be - accessing loaded content like it was present directly inside the clip it was loaded in. But that doesn't work at all.
So to rephrase, my question is:
How do I communicate from the main "loader" SWF file, with the content that's loaded in. I don't want to communicate using event.target.content.{etc} because then I'd only be able to address the loaded content inside the Loader's event.complete event handler.
How do I "re-parent" loaded content, so I can place it inside some movieclip/sprite on the main timeline of the loader file, rather than using some really long convoluted way.
How to communicate from inside the loaded content, to the main/loader file. Previously, we'd use _root.functionName() to do stuff like play some animation transitioning from the current externally loaded "section" to another section. How'd I go about doing that.
AS2 & AS3 is vastly different. But you will have to swallow the fact that AS3 has been developed as an improvement over AS2. So any transition you make, is also for the better.
For eg : The _root in AS2 allowed global objects & variables to accessed & changed anywhere, which is a bad practice & leads to non maintainable code in a project.
Having said that, let me address your questions:
If you are able to get access to the loaded content with
event.target.content... you should save it inside a,say class
variable & may access it later elsewhere in the class.
You must understand that you will be able to access the content only
after loading it, so have to wait for it to complete anyway &
event.complete handler is probably your best bet.
I doubt if you can pick random content from a loaded swf & re-parent it into the current swf.As explained you might not have a long convoluted way.
Accessing the parent could be done in many ways. You can use .parent or actually call a function from the parent swf passing its reference to the child.
var sprite;
addChild(sprite);
var loader:Loader = new Loader();
loader.load(new URLRequest("ExternalContent.swf"));
loader.contentLoaderInfo.addEventListener(Event.COMPLETE,onLoaded);
function onLoaded(event:Event):void
{
sprite = event.target.content;
//This should work
sprite.boxes.boxesPrompt();
}
See this example for more info.

Adding an intro to a swf without access to the fla OR: differences between top-level and loaded swfs

I've got to (quickly!) add an intro to an existing flash masthead on a site that I've inherited. I don't have access to the source .fla's, so I approached the problem by putting the intro in a wrapper swf and loading the current masthead and adding it to the display list on Event.INIT. So far, so good. (Incidentally, the swfs are built for flash player 9 and use AS3.)
The problem I'm having is that although the intro plays fine and loads / displays the beginning of the masthead swf, which is a loading animation, the masthead itself never actually plays. Essentially, my question is: what would cause an actionscript 3-based swf to behave differently when it's the child of another swf as opposed to at the top level of the embedded swf?
Potentially important details: Embedding is being handled with swfobject, and no flashvars are being passed in. There are two params, which are base: "/flash/" and wmode: "opaque". All the swfs and flash data live in /flash/. The flash elements (minus the intro I built) were constructed using the Inky flash framework, with which I'm not familiar.
UPDATE: I've reconsidered my approach to the problem and gotten it working by using ExternalInterface; I'm having the intro swf call a js function when it finishes playing, which swaps out the intro swf and replaces it with the current masthead (the approach is outlined here). I'd still like to know why I was witnessing the behavior I was seeing earlier, though, so any ideas and suggestions would be welcome.
There could be a couple of potential problems with your flash files.
If your swf was compiled in AS2, it could be referencing _root which would be messed up when it is loaded into another swf. In AS2, you can get around it by using _lockroot. In AS3, this is not longer a problem because _lockroot is inheritted.
If your movie was a timeline based movie, you can try to invoke the play() function.
If your intro loads external assets, you will want to make sure your paths are set correctly for all your external assets. Try to put the swf file of your container and the intro in to the same directory. Or troubleshoot by using the Safari Activity window to see if you have any "404 not found".
Another thing is inconsistent flash version. You could run into problem if you load a Flash version 9 with a Flash version 10.
Hope these pointers help.