// ———————————————————————–
// IFrame.as – Alistair Rutherford, www.netthreads.co.uk
// Glasgow, Scotland Oct 2007 .
// ———————————————————————–
// Revision Date Notes
// ——– —- —–
// 1.0 16/09/07 .Initial version
// 1.1 29/09/07 .Fixed bug where the frame wasn’t resizing itself
// when the source url was assigned.
// ———————————————————————–
// This component is based on the work of:
//
// Christophe Conraets
// www.coenraets.org
//
// and
//
// Brian Deitte
//
http://www.deitte.com/archives/2006/08/finally_updated.htm
//
// ———————————————————————–
// I have made some additions to the original code
//
// – javascript support functions are now generated by the component and
// inserted directly into the DOM.
//
// – Component generates it’s own div and iframe element and inserts them
// into the DOM.
//
// – When the component is created the display list is traversed from the
// component down to the root element. At each traversal a test is made to
// see if current component is a container. If it is a container then the
// child of the element which leads back to the component is determined and
// a note madeof the appropriate ‘index’ on the path. The index is stored
// against a reference to the Container in a Dictionary. Also the container
// is ‘seeded’ with an event handler so that if the container triggers an
// IndexChangedEvent.CHANGE (i.e. when you click on a tab in a viewstack)
// the path of ‘index’ values down to the component can be checked. If the
// path indicates that the indexes ‘line up’ to expose the component then
// the view is made visible. I hope I have explained this correctly

// ———————————————————————–
package
{
import flash.geom.Point;
import flash.events.Event;
import flash.utils.Dictionary;
import flash.display.DisplayObjectContainer;
import mx.core.Container;
import mx.events.IndexChangedEvent;
import flash.external.ExternalInterface;
public class IFrame extends Container
{
private var __source: String;
private var frameId:String;
private var iframeId:String;
private var containerDict:Object = null;
private var settingDict:Object = null;
/**
* Here we define javascript functions which will be inserted into the DOM
*
*/
private static var FUNCTION_CREATEIFRAME:String =
“document.insertScript = function ()” +
“{ ” +
“if (document.createIFrame==null)” +
“{” +
“createIFrame = function (frameID)” +
“{ ” +
“var bodyID = document.getElementsByTagName(\”body\”)[0];” +
“var newDiv = document.createElement(‘div’);” +
“newDiv.id = frameID;” +
“newDiv.style.position =’absolute’;” +
“newDiv.style.backgroundColor = ‘transparent’;” +
“newDiv.style.border = ’0px’;” +
“newDiv.style.visibility = ‘hidden’;” +
“bodyID.appendChild(newDiv);” +
“}” +
“}” +
“}”;
private static var FUNCTION_MOVEIFRAME:String =
“document.insertScript = function ()” +
“{ ” +
“if (document.moveIFrame==null)” +
“{” +
“moveIFrame = function(frameID, iframeID, x,y,w,h) ” +
“{” +
“var frameRef=document.getElementById(frameID);” +
“frameRef.style.left=x;” +
“frameRef.style.top=y;” +
“var iFrameRef=document.getElementById(iframeID);” +
“iFrameRef.width=w;” +
“iFrameRef.height=h;” +
“}” +
“}” +
“}”;
private static var FUNCTION_HIDEIFRAME:String =
“document.insertScript = function ()” +
“{ ” +
“if (document.hideIFrame==null)” +
“{” +
“hideIFrame = function (frameID)” +
“{” +
“document.getElementById(frameID).style.visibility=’hidden’;” +
“}” +
“}” +
“}”;
private static var FUNCTION_SHOWIFRAME:String =
“document.insertScript = function ()” +
“{ ” +
“if (document.showIFrame==null)” +
“{” +
“showIFrame = function (frameID)” +
“{” +
“document.getElementById(frameID).style.visibility=’visible’;” +
“}” +
“}” +
“}”;
private static var FUNCTION_LOADIFRAME:String =
“document.insertScript = function ()” +
“{ ” +
“if (document.loadIFrame==null)” +
“{” +
“loadIFrame = function (frameID, iframeID, url)” +
“{” +
“document.getElementById(frameID).innerHTML = \”<iframe id=’\”+iframeID+\”‘ src=’\”+url+\”‘ frameborder=’0′></iframe>\”;” +
“}” +
“}” +
“}”;
/**
* Constructor
*
*/
public function IFrame()
{
super();
}
/**
* Generate DOM elements and build display path.
*
*/
override protected function createChildren():void
{
super.createChildren();
if (! ExternalInterface.available)
{
throw new Error(“ExternalInterface is not available in this container. Internet Explorer ActiveX, Firefox, Mozilla 1.7.5 and greater, or other browsers that support NPRuntime are required.”);
}
// Generate unique id’s for frame div name
frameId = id;
iframeId = “iframe_”+frameId;
// Add functions to DOM if they aren’t already there
ExternalInterface.call(FUNCTION_CREATEIFRAME);
ExternalInterface.call(FUNCTION_MOVEIFRAME);
ExternalInterface.call(FUNCTION_HIDEIFRAME);
ExternalInterface.call(FUNCTION_SHOWIFRAME);
ExternalInterface.call(FUNCTION_LOADIFRAME);
// Insert frame into DOM using our precreated function ‘createIFrame’
ExternalInterface.call(“createIFrame”, frameId);
buildContainerList();
}
/**
* Build list of container object on the display list path all the way down
* to this object. We will seed the container classed we find with an event
* listener will be used to test if this object is to be displayed or not.
*
*/
private function buildContainerList():void
{
// We are going to store containers against index of child which leads down
// to IFrame item.
containerDict = new Dictionary();
settingDict = new Dictionary();
var current:DisplayObjectContainer = parent;
var previous:DisplayObjectContainer = this;
while (current!=null)
{
if (current is Container)
{
if (current.contains(previous))
{
var childIndex:Number = current.getChildIndex(previous);
// Store child index against container
containerDict[current] = childIndex;
settingDict[current] = childIndex;
// Tag on a change listener
current.addEventListener(IndexChangedEvent.CHANGE, handleChange);
}
}
previous = current;
current = current.parent;
}
}
/**
* Triggered by one of our listeners seeded all the way up the display
* list to catch a ‘changed’ event which might hide or display this object.
*
* @param event Event trigger
*
*/
private function handleChange(event:Event):void
{
var target:Object = event.target;
if (event is IndexChangedEvent)
{
var changedEvent:IndexChangedEvent = IndexChangedEvent(event)
var newIndex:Number = changedEvent.newIndex;
visible = checkDisplay(target, newIndex);
}
}
/**
* This function updates the selected view child of the signalling container
* and then compares the path from our IFrame up the displaylist to see if
* the index settings match. Only an exact match all the way down to our
* IFrame will satisfy the condition to display the IFrame contents.
*
* @param target Object event source
* @param newIndex Number index from target object.
*
*/
private function checkDisplay(target:Object, newIndex:Number):Boolean
{
var valid:Boolean = false;
if (target is Container)
{
var container:DisplayObjectContainer = DisplayObjectContainer(target);
// Update current setting
settingDict[container] = newIndex;
valid = true;
for (var item:Object in containerDict)
{
var index:Number = lookupIndex(item as Container);
var setting:Number = lookupSetting(item as Container);
valid = valid&&(index==setting);
}
}
return valid;
}
/**
* Return index of child item on path down to this object. If not
* found then return -1;
*
* @param target Container object
*
*/
public function lookupIndex(target:Container):Number
{
var index:Number = -1;
try
{
index = containerDict[target];
}
catch (e:Error)
{
// Error not found, we have to catch this or a silent exception
// will be thrown.
trace(e);
}
return index;
}
/**
* Return index of child item on path down to this object. If not
* found then return -1;
*
* @param target Container object
*
*/
public function lookupSetting(target:Container):Number
{
var index:Number = -1;
try
{
index = settingDict[target];
}
catch (e:Error)
{
// Error not found, we have to catch this or a silent exception
// will be thrown.
trace(e);
}
return index;
}
/**
* Adjust frame position to match the exposed area in the application.
*
*/
private function moveIFrame(): void
{
var localPt:Point = new Point(0, 0);
var globalPt:Point = this.localToGlobal(localPt);
ExternalInterface.call(“moveIFrame”, frameId, iframeId, globalPt.x, globalPt.y, this.width, this.height);
}
/**
* Triggered by change to component properties.
*
*/
override protected function commitProperties():void
{
super.commitProperties();
if (source)
{
ExternalInterface.call(“loadIFrame”, frameId, iframeId, source);
// Trigger re-layout of iframe contents.
invalidateDisplayList();
}
}
/**
* Triggered when display contents change. Adjusts frame layout.
*
* @param unscaledWidth
* @param unscaledHeight
*
*/
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
moveIFrame();
}
/**
* Set source url
*
* @param url Frame contents
*
*/
public function set source(source: String): void
{
if (source)
{
__source = source;
invalidateProperties();
}
}
/**
* Return url of frame contents
*
*/
public function get source(): String
{
return __source;
}
/**
* Sets visibility of html iframe. Rtn calls inserted javascript functions.
*
* @param visible Boolean flag
*
*/
override public function set visible(visible: Boolean): void
{
super.visible=visible;
if (visible)
{
ExternalInterface.call(“showIFrame”, frameId);
}
else
{
ExternalInterface.call(“hideIFrame”, frameId);
}
}
}
}
<mx:Panel width=”100%” height=”100%” title=”Content” paddingTop=”1″ paddingBottom=”1″ paddingLeft=”1″ paddingRight=”1″ >
<local:IFrame id=”iFrame” source=”http://www.adobe.com/devnet/flex/” width=”100%” height=”100%”/>
<mx:ControlBar>
<mx:CheckBox id=”cbVisible” label=”IFrame Visible” selected=”true” click=”iFrame.visible=cbVisible.selected”/>
</mx:ControlBar>
</mx:Panel>