Archive for the ‘ActionScript and JavaScript Sitting In A Tree…’ Category

External Interface Mastery

Friday, May 14th, 2010

Mastering the External Interface.

It surprises me how many people know so little about the External Interface and what can be done with it.
You can communicate back and forth from ActionScript to JavaScript using the External Interface.
Many wonderful things are possible.

I have made a class called DOMEx that utilizes the External Interface and JavaScript stored as XML to aid in the communication between JavaScript and ActionScript.

To get a quick idea of some of the things that can be done, you can view some of the examples I’ve made using the DOMEx set of classes.

Dragging a green circle outside and back into the flash

Three D Cube following the mouse no matter where the mouse is

The swf responding to the wrappers key events

Basic Overview

Basic use of the External Interface.

import flash.external.ExternalInterface;

if (ExternalInterface.available) ExternalInterface.call("function () { alert('Hello World'); }");

//or

var javaScript:XML =
<script>
<![CDATA[
function (words) {

   alert(words);

}
]]>
</script>;

if (ExternalInterface.available) ExternalInterface.call(javaScript, "Howdy Ya'll");

//and

var browserLocation:String = ExternalInterface.call("window.location.toString()");
trace(browserLocation);

//adding a callback
if (ExternalInterface.available) ExternalInterface.addCallback("javaScriptCalling", myActionScriptFunction);

function myActionScriptFunction(...args):void {

//do something in the swf

}

Things To Know
The External Interface has been around for some time now, (Flash 8) but got popular with AS3.

You can pass, Strings, Numbers, Booleans, Arrays, basic Objects, and XML back and forth between ActionScript and JavaScript.
XML is converted to a string when accessed via JavaScript, and sometimes has a hard time getting back to an ActionScript form.
When communicating between the two, it is best to just stick to the main types of Objects (Strings, Numbers, Arrays)

When adding a call back for javaScript to call, there are some words that are reserved and will not work in Internet Explorer as callbacks.
GetVariable, SetVariable, Play, and most of the words found here as methods or properties:

http://www.adobe.com/support/flash/publishexport/scriptingwithflash/scriptingwithflash_03.html

When testing in the Flash IDE, ExternalInterface.available may return true.
You can test against that by doing something like this:

var javascript:XML=<script><![CDATA[function(){return ["some", "array"];}]]></script>;
if (ExternalInterface.available && ExternalInterface.call(javascript) == null) {
    //you may be in the IDE
}

The DOMEx Class
The DOMEx Class makes it possible to easily communicate back and forth between AS and JS,
without having to set anything up in the wrapper.
The documentation is here: DOMEx Documentation

The atadbHashManager

Tuesday, April 27th, 2010

This is the specific hash manager for the actiontad banner. It uses the onHashChangeManager for the main functionality and also does things specific to the layout of the site and the hash values. It is the final step in the marriage between the onhashchange JavaScript event and the SWFs loading of XML based on the hash value.

package
{

	import flash.display.*;
	import flash.utils.Timer;
	import flash.events.TimerEvent;
	import flash.events.Event;

	import com.tadSrc.tadsClasses.onHashChangeManager;
	import com.tadSrc.tadsClasses.DOMEx;
	import com.tadSrc.tadsClasses.LoadSomeXML;

	public class atadbHashManager
	{

		public var onHashDispatch:onHashChangeManager;
		public var memory:Array = [];
		public var whosGotIt:String = "server";

		public var scriptsToUse:Class = bannerScripts; /* of public static javascript in xml function vars */ 

		public function atadbHashManager(whosGotItClientOrServer:String, scripts:Class = null)
		{
			if (scripts != null) scriptsToUse = scripts;
			whosGotIt = whosGotItClientOrServer;

			if (whosGotIt == "client") {

				/* whosGotIt will be client if the correct browser config is met
				   this is determined by the server, and passed in as flashvars */

				/* A new onHashChangeManager is instantiated with deliverContent as the function
				   that will happen when the hash changes.
				   A 1000 millisecond lag time is applied to help ensure the DOM is ready. */

				onHashDispatch = new onHashChangeManager(deliverContent, 1000);

				//after 400 milliseconds firstDelivery is called
				var t:Timer = new Timer(400, 1);
				t.addEventListener(TimerEvent.TIMER_COMPLETE, firstDelivery, false, 0, true);
				t.start();

			} 

		}

		private function firstDelivery(e:TimerEvent):void {

			var firstHashLook:String =
			(DOMEx.call(scriptsToUse.getHash)+"").toString().replace(" ", "").toLowerCase();

			DOMEx.call(scriptsToUse.changeAnchors, firstHashLook, whosGotIt);
			if (firstHashLook != "") {
				deliverContent(firstHashLook);
			}

			e.target.removeEventListener(TimerEvent.TIMER_COMPLETE, firstDelivery);

		}

		public function deliverContent(h:String = ""):void {

			var contentToGet:String = "welcome";
			var chash:String = h.toLowerCase();

			contentToGet = ( chash != "") ? chash : contentToGet;

			 if (DOMEx.call(scriptsToUse.innerAlready, contentToGet) == false) {
				if (memory.indexOf(contentToGet) == -1) {
					DOMEx.call(scriptsToUse.loadSayToggle);
					memory.push(contentToGet);
					LoadSomeXML.freshXMLFile("themerz/"+contentToGet+".xml", insertContent, loadError);
				} else {

					DOMEx.call(scriptsToUse.putIn, memory[memory.indexOf(contentToGet)+1]);
				}
			  }

			DOMEx.call(scriptsToUse.changeAnchors, contentToGet, whosGotIt);
			if (DOMEx.call(scriptsToUse.anyPres) == true) DOMEx.resizeMakeAbleHorizontal("pre");

		}

		private function insertContent(e:Event = null):void {

			var newContent:XML = new XML(e.target.data);
			var astring:String = newContent.thedata.toString();
			var makemine:RegExp = new RegExp("a8[a-z]{2,}8a", "i");
			//if the data is that of an example, the dagtcmpuA function determines which
			//version of the swf to place, IE or not, and colors the code.
			if (astring.match(makemine))
                          astring = DOMEx.call(dagtcmpuA(), astring, memory[memory.length-1]);

			DOMEx.call(scriptsToUse.putIn, astring);

			memory.push(astring);

			if (DOMEx.call(scriptsToUse.anyPres) == true) DOMEx.resizeMakeAbleHorizontal("pre");
		}

		private function loadError(e:Event = null):void {

			//DOMEx.call("alert", memory[memory.length-1].toString()+" not found.");
			memory.pop();
		}

	}
}

There are a good amount of JavaScript in XML variables used in this process, so I’ve put them in their own class, called bannerScripts.

package
{

	public class bannerScripts
	{

		public static var putIn:XML =
			<script>
			<![CDATA[
			function (astring) {
				document.getElementById("top").innerHTML = astring;
			}
			]]>
			</script>;

		public static var anyPres:XML =
			<script>
			<![CDATA[
			function (astring) {
				var pr = false;
				if (document.getElementsByTagName("pre").length > 0) pr = true;
				return pr;
			}
			]]>
			</script>;

		public static var loadSayToggle:XML =
			<script>
			<![CDATA[
			function () {
				var ciner = document.getElementById("top").innerHTML.toString();
				document.getElementById("top").innerHTML = "Loading..."+ciner;
			}
			]]>
			</script>;

		public static var innerAlready:XML =
			<script>
			<![CDATA[
			function (currentContentName) {
				var r = false;
				var ciner = document.getElementById("top").innerHTML.toString();
				var checkFor = "id=\""+currentContentName;
				if (ciner.indexOf(checkFor) != -1) r = true;
				return r;
			}
			]]>
			</script>;

		public static var getHash:XML =
			<script>
			<![CDATA[
			function ()
			{
					if (window.location.toString().match("#"))
					{
						var afthash = new RegExp("[\#]{1}[a-z0-9]{1,}", "i");
						var thehashword =
							window.location.toString().match(afthash).toString().
							replace("#", "").toLowerCase();
						return(thehashword);
					}
					else {return("");}
			}
			]]>
			</script>;

		public static var changeAnchors:XML =
			<script>
			<![CDATA[
			function (current, statusWho) {

				var allAs = document.getElementsByTagName("a");
				var numAs = allAs.length;
				var napa = navigator.appName+navigator.appVersion+"";
				var isie = (napa.indexOf("MSIE") != -1 || napa.indexOf("Explorer") != -1) ? true : false;
				var properBrow = ((isie == true && napa.indexOf("8.0") != -1) || isie == false) ? true : false;

				for (var i = 0; i < numAs; i++)
				{
					var curA = allAs[i];

					if (current != "") {
						if (curA.className == "ancselec" || curA.className == "ancaselec")
							curA.className = curA.className.replace("selec", "");

						if (curA.id == ""+current+"a")
							curA.className = curA.className+"selec";
					}

					if (statusWho == "client" && properBrow == true &&
								curA.href.toString().indexOf("plain/?section=") != -1) {

						   curA.href = curA.href.toString().replace("plain/?section=", "#");
					}
				}

			}
			]]>
			</script>;

	}

}

Server side setup for onhashchange support

Monday, April 26th, 2010

This code is used to determine if the browser is IE 8, FF 3.6+ or Google Chrome 4+.

If it is one of those browsers,
then the string “client” is passed to the swf via flash vars,
and the swf handles delivering content to the page with hash values.

Otherwise it is the server that will deliver the content of the site as normal, if the user does not have one of the above browsers.

$status = "theServer";
if (isset($_SERVER['HTTP_USER_AGENT'])) {

    $agent = $_SERVER['HTTP_USER_AGENT'];

    $goodBrowser = '/FireFox|MSIE|Google|Chrome|Internet[ ]{0,}Explorer/i';

    $goodVersion = '/8[\.]{0,}[0-9]{0,}|3[\.]{1}[6789]{0,}|9[\.]{1}[0-9]{0,}/';

    $gChrome = '/chrome/i';

    $gGoogle = '/google|4[\.]{1}1/i';

    if (preg_match($goodBrowser, $agent,  $agentMatch)) {

         if (preg_match($goodVersion, $agent)) { $status = "client"; }

         if (preg_match($gChrome, $agent) && preg_match($gGoogle, $agent)) { $status = "client"; }

    }

}

<!-- at the end of the page -->
params.flashvars = "from=<?php echo($status); ?>";