Dynamically Resizing Your Flex Application
This post could also be titled: Using the browser scroll bar with a Flex Application.
A few years ago I was struggling with getting the Mac Wheel and the PC Wheel to work with a Flex App I had made. So rather than getting both of those inputs to work, I just had the Flex app dynamically change the size of the browser and used the browser’s internal scrollbars to have the user go up and down the application.
This may sound difficult but it isn’t, just mix and match ExternalInterface with a few nice javascript calls and you’re good to go.
If you want to see the final product just follow this link. You’ll need to just enter in the size you want to resize your app to and hit the button.
So let’s look at what we’ve put together.
First I wanted this class to be a Singleton. I did that because I wanted to be able to access editing the size of the application from anywhere – and as there is only one browser instance – a Singleton made sense.
Now there are at minimum two javascript functions that I will need to create for this example to work. The first function will need to pull the application’s id so that we can reference that id in javascript. The second javascript function will need to resize the flash application.
To get these javascript functions to work though we need to inject them into the dom. This is actually easier than you would think.
What I did was use the ExternalInterface to call a function to check if the javascript function existed. If it doesn’t, add it into the dom. Then from that point on you just need to call the function directly. This strategy is very helpful because you don’t have to change the template and make sure the functions already exist, instead you add them into your HTML page at runtime.
Once the functions exist you just call them and you’re done.
Here is the source for the Singleton class I’ve created. I will let you know now that some of the javascript functionality was stolen from the iFrame Google Code class. They did a great job!
{
import flash.external.ExternalInterface;
/**
* Resized the Browser.
* @author jonbcampos
*
*/
public class BrowserSizeManager
{
//---------------------------------------------------------------------
//
// Singleton Methods
//
//---------------------------------------------------------------------
private static var _instance:BrowserSizeManager;
public function BrowserSizeManager(enforcer:SingletonEnforcer)
{
//set up function
ExternalInterface.call(INSERT_FUNCTION_ASK_FOR_EMBED_OBJECT_ID);
ExternalInterface.call(INSERT_FUNCTION_RESIZE_FLASH_APP);
//get application id
_resolveEmbedObjectId();
}
public static function getInstance():BrowserSizeManager
{
if(!_instance)
_instance = new BrowserSizeManager(new SingletonEnforcer());
return _instance;
}
//---------------------------------------------------------------------
//
// Private Properties
//
//---------------------------------------------------------------------
private var _applicationId:String;
private var _randomIdentificationString:Number;
//---------------------------------------------------------------------
//
// Javascript Methods
//
//---------------------------------------------------------------------
private static const FUNCTION_RESIZE_FLASH_APP:String = "resizeFlashApp";
private static var INSERT_FUNCTION_RESIZE_FLASH_APP:String =
"document.insertScript = function ()" +
"{ " +
"if (document."+FUNCTION_RESIZE_FLASH_APP+"==null)" +
"{" +
FUNCTION_RESIZE_FLASH_APP+" = function (embedId, height)" +
"{ " +
"var flashApp = document.getElementById(embedId);" +
"flashApp.style.height = height+'px';" +
"}" +
"}" +
"}";
private static const FUNCTION_ASK_FOR_EMBED_OBJECT_ID:String = "askForEmbedObjectId";
/**
* The Javascript code to call to insert the function that prompts the DOM objects
* to find the SWF object id.
*/
private static var INSERT_FUNCTION_ASK_FOR_EMBED_OBJECT_ID:String =
"document.insertScript = function ()" +
"{ " +
"if (document."+FUNCTION_ASK_FOR_EMBED_OBJECT_ID+"==null)" +
"{ " +
FUNCTION_ASK_FOR_EMBED_OBJECT_ID+" = function(randomString) " +
"{ " +
"try { " +
"var embeds = document.getElementsByTagName('embed'); " +
"for (var i = 0; i < embeds.length; i++) { " +
"var isTheGoodOne = embeds[i].checkObjectId(embeds[i].getAttribute('id'),randomString); " +
"if(isTheGoodOne) { " +
"return embeds[i].getAttribute('id'); " +
"} " +
"} " +
"var objects = document.getElementsByTagName('object'); " +
"for(i = 0; i < objects.length; i++) { " +
"var isTheGoodOne = objects[i].checkObjectId(objects[i].getAttribute('id'),randomString); " +
"if(isTheGoodOne) { " +
"return objects[i].getAttribute('id'); " +
"} " +
"} " +
"} catch(e) {} " +
"return null; " +
"} " +
"} " +
"}";
//---------------------------------------------------------------------
//
// Public Methods
//
//---------------------------------------------------------------------
/**
* Resizes the browser to a set size.
* @param height
*
*/
public function resizeBrowser(height:Number):void
{
ExternalInterface.call(FUNCTION_RESIZE_FLASH_APP, _applicationId, height);
}
//---------------------------------------------------------------------
//
// Private Methods
//
//---------------------------------------------------------------------
/**
* @private
* Get's the application Id.
*
*/
private function _resolveEmbedObjectId():void
{
if(!_applicationId)
{
try
{
_randomIdentificationString=Math.ceil(Math.random()*9999*1000);
ExternalInterface.addCallback('checkObjectId', checkObjectId);
var result:Object=ExternalInterface.call(FUNCTION_ASK_FOR_EMBED_OBJECT_ID, _randomIdentificationString.toString());
if(result!=null)
{
_applicationId=String(result);
} else {
//dang... fail
}
} catch (error:Error)
{
//dang... fail
}
}
}
//---------------------------------------------------------------------
//
// Protected Methods
//
//---------------------------------------------------------------------
/**
* Return check that the random number's match.
* @param id
* @param randomCode
* @return
*
*/
protected function checkObjectId(id:String, randomCode:Number):Boolean
{
return (_randomIdentificationString==randomCode);
}
}
}
class SingletonEnforcer{}
Further Development:
To continue making the class just perfect you could also have the class return the current size of the browser so that the flex app isn’t set to lower than the visible browser. Also you could have a listener so when the browser resizes, the app resizes (because once you set the height, it’s fixed – unless you set back to 100%). You could also set up a queue system so that functions aren’t called multiple times on within a frame, just wait for the next Enter Frame.






There’s opportunity for semantic improvement:
1. Foremost you shouldn’t need to query for your movie element ID. I believe (although now I wonder what if it doesn’t exist) it’s always statically accessible in ExternalInterface.objectID.
2. There are at least three techniques (ordered by least to favourite preference) to beautify your polyglot JavaScript improving maintainability:
a) Multiline strings.
static const JAVASCRIPT:String = “function(objectID) {\
alert(objectID);\
}”;
b) Interpolated strings using a XMLList literal which Eclipse and TextMate (but not intelliJ because it has a stronger AST) incidentally syntax highlights.
static const JAVASCRIPT:String = function() {
alert({ExternalInterface.objectID});
};
c) Embedded asset strings.
[Embed(source="assets/javascript.js", mimeType="application/octet-stream")]
static const JavaScript:Class;
static const JAVASCRIPT:String = new JavaScript().toString();
3. Save some typing and use my ExternalInterfaceProxy instead of invoking it all manually
http://github.com/dnalot/as3-dnalot/blob/master/src/Tester.mxml
[...] This post was mentioned on Twitter by CT, tony murphy. tony murphy said: Latest Adobe News – Dynamically Resizing Your Flex Application http://feedproxy.google.com/~r/TheWorldInAStateOfFlex/~3/faxIDb2pipY/ [...]
@Jon
1. Cool, never used that. That would make things easier.
2. Good comments. When it comes to the Javascript, I prefer the 3rd option, I just stayed away from it to make it easiest to read
PS 2.b)’s E4X got eaten by WP so trying again for posterity:
static const JAVASCRIPT:String = <>function() {
alert({ExternalInterface.objectID});
}</>;
REALLY GREAT JOB!
I was looking for such solution for days! I really appreciate it! Thanx
Thanks, look very useful.
I don’t know yet how to use it from my flex application, can someone write down an example of how to call the class? Thank you very much
Did you not see the example provided?
Hi!
I’m using the Flex SDK 3.0 and it only works in the debug mode! I also tested it with SDK 4.0. Same problem! What I can do to fix this prob?
Thanks
Jack
@jack Only works in debug mode? In what way are you using this? There may be an issue that the application is still rendering and doesn’t know it’s proper size when it is sending it out to javascript. The debugging may be adding a wait to the process giving it time to get the actual size you need, but that is just a guess.
Hi Jonathan!
> Only works in debug mode?
- Yes, only in debug mode.
> In what way are you using this?
- I’m using only your example code with FlexBuilder IDE 4.
But your example with SDK 4 above works! And I think, that’s not a debug version? :/
It works! The problem is the ActionScript Security Error 206! It is s security sandbox violation. In the HTML make sure that the parameter ‘allowscriptaccess’ is set to ‘always’ rather than ‘sameDomain’ or ‘never’ in all cases or upload your Files to a webserver:
WRONG HTML Code using swfObject 2.0:
so.addParam(‘allowscriptaccess’, ‘sameDomain’);
CORRECT HTML Code using swfObject 2.0:
so.addParam(‘allowscriptaccess’, ‘always’);
###################################
WRONG HTML Code using AC_FL_RunContent:
‘allowScriptAccess’,'sameDomain’,
CORRECT HTML Code using AC_FL_RunContent:
‘allowScriptAccess’,'always’,
###################################
WRONG HTML Code using embed tag:
allowscriptaccess=”sameDomain”
CORRECT HTML Code using embed tag:
allowscriptaccess=”always”
###################################
WRONG HTML Object tag:
CORECT HTML Object tag:
@ Jonathan can u add this tags to my upper post and delete this one plz!
WRONG HTML Object tag:
CORRECT HTML Object tag:
Ohh you’re blocking html tags I forget ^^
Ok here without brakets:
WRONG HTML Object tag:
param name=”allowScriptAccess” value=“sameDomain” /
CORRECT HTML Object tag:
param name=”allowScriptAccess” value=“always” /
@Jack great find!
Doesn’t work in the latest firefox (6) and Flash (10.3.181…)