CMS Flash: A Blue Eye for the Duck
A fine Flash artist asks:
Can I script her site so that clients can make minor adjustments without her?
Well, how minor?
Like the changes they want again and again during development. One pic in place of another. Different wording in a blurb. Like that.
Yes, I say. The answer is always Yes. Yes, software can tie your shoe, if you invest enough. Then the question shifts. What would the client pay for that?
Make or break, says the Flash artist. They might never use this. But they might not want my site if I’m the only one who can fix every little thing.
I see. I know the feeling. I don’t want in until I know a way out. Don’t want to feel trapped.
The client may never use this, but won’t proceed without it. Won’t use, and won’t skip.
Ok, I get that. Understanding the tech tools is always the least of our challenges. We think people make no sense whenever they make more sense than we can keep up with.
So I research Flash CMS. Am I wrong, or are these just Easy-Flash-Substitutes by another name? If you start with this or that Easy, then changes are, well, Easy. What starts Easy stays Easy. Start there, stay there, trapped in Easy.
No use to my Flash artist.
She shows me a Fla with three dozen layers on the timeline, and three dozen symbols in the library. For every symbol in the library, another few texts or whatever play along the timeline without showing in the library. Static Text, many of them.
How script her work without breaking it? Then, how get each runtime text or picture to the right place inside the whirling shooting gallery of Flash?
First, promote Static Text to Dynamic Text. Script can do nothing with Static Text.
Ah, Dynamic Text can render HTML, I see. Including the <img> tag. Great then, we promote bitmaps to Dynamic Text as well, and set their htmlText to <img…>. One trick to replace both pictures and texts.
Now, how get new HTML to those text fields, from outside Flash at runtime?
For the moment I won’t detail all my false starts. XML, to begin with. Easy enough to get XML into Flash at runtime, including HTML wrapped in a cheerful and welcoming <![CDATA[ and ]]>.
But Flash is a shooting gallery inside. Your target comes and goes, on top of (inside of) something else that comes and goes, like ducks in a shooting gallery, on a tiny ferris wheel that jogs into view from one side and vanishes at the other. The eye of your duck opens and closes as he rises and falls on the ferris wheel. You have a blue eye for the red-eyed duck, thanks to XML, but first you must catch him.
My XML told me how to find him. I felt like a bicycle messenger in Manhattan. What room, what building, what street. And what hours? No use to find the place when your guy is out. Where then? Find the container of the container of the container. The Over state of a SimpleButton, for instance, with another busy container inside, and our duck among the jumble there. And what hours? I needed earliest frame and latest frame for that button.
Yes, at last I learned to run down that duck and deliver his new blue eye. But what if he moved to another address? If my Flash artist edited the Fla much, I had to rewrite the directions to my duck.
Meanwhile I found a good demo of HTML in Flash. My Flash artist was impressed. Hmmm, I thought, he is delivering new HTML to Flash from JavaScript.
Flash supplies an ExternalInterface for talking to JavaScript in the browser that runs Flash.
Suppose instead of chasing down that duck to deliver a new blue eye, I taught the duck to come get it?
So I wrote a TextFieldJS class. It contains my TextField (the Dynamic Text promoted from Static Text) and the ExternalInterface. TextFieldJS looks to the JavaScript in the browser for its runtime value.
That JavaScript looks like this:
<script type="text/javascript">var jsReady = false;var arrHtml = new Array();arrHtml["FrontPic"] = "<img src=’istockphoto_4383679_petri_dishes_for_medical_research.jpg’ width=’380′ height=’253′>";arrHtml["FrontBlurb"] = "A multi-media presentation incorporating 14 Cultural Trends including:<li>Trends behaviors and attitudes</li><p><li>Underlying psychological need states</li><p><li>Emphasis on why? vs. what?</li><p><li>Evolving cultural examples from food, fashion, music, tech and retail arenas</li><p><li>Customized concepts using trends for your brand</li><p><li>Group dynamic Trend Exercises</li>";function isReady() {return jsReady;}function pageInit() {jsReady = true;}function getValue(aID) {return arrHtml[aID];}</script>
And to tell Flash it’s ready:
<body onload=”pageInit()”>
Each TextFieldJS calls getValue() with its own unique name, “FrontPic” or “FrontBlurb” in this example.
From the browser, JavaScript responds with new HTML for the TextField. In the case of “FrontPic” that’s
<img src=’istockphoto_4383679_petri_dishes_for_medical_research.jpg’ width=’380′ height=’253′>
Now we’re updating text and pictures inside Flash at runtime.
Here’s TextFieldJS
package cerium {import flash.display.*;import flash.text.*;import flash.external.ExternalInterface;import flash.utils.Timer;import flash.events.*;//sprite contains our TextField; convert TextField to symbol (MovieClip);//whether TextField shows bitmap or html text;public class TextFieldJS extends Sprite {//m_TextField.htmlText = ExternalInterface.call(kJavaScriptFunction,m_ID);//calls JavaScript getValue(aID) in HTML shared by entire Flash movie and every TextFieldJS therein;//therefore need string IDprivate const kJavaScriptFunction:String = "getValue";//no parenspublic var m_ID:String;//as known to JavaScript//TextField we converted to symbol:public var m_TextField:TextField;//unless Automatically Declared Stage Instancesprivate var m_TickCount:int;public function TextFieldJS() {m_TextField.border = false;m_TextField.background = false;m_TextField.alpha = 1;m_TextField.multiline = true;m_TextField.wordWrap = true;if(ExternalInterface.available) {try {//ask browser if JavaScript ready:var isJavaScriptReady:Boolean = isJavaScriptReady();if (isJavaScriptReady) {getValue();}else {m_TextField.text = "JavaScript not ready. Starting timer.";//timer calls browser at 100ms intervals;//once the browser replies ready, stop timer;m_TickCount = 0;var aTimer:Timer = new Timer(100);aTimer.addEventListener(TimerEvent.TIMER,onTimerTick);aTimer.start();}}catch(aError:SecurityError) {m_TextField.text = "Security error: " + aError.message;}catch(aError:Error) {m_TextField.text = "Error: " + aError.message;}}else {m_TextField.text = "No external interface available.";}//end available}private function getValue():void {m_TextField.text = "";m_TextField.htmlText = "";m_TextField.htmlText = ExternalInterface.call(kJavaScriptFunction,getID());}private function isJavaScriptReady():Boolean {var isReady:Boolean = ExternalInterface.call("isReady");return isReady;}private function onTimerTick(aEvent:TimerEvent):void {m_TickCount++;m_TextField.text = m_TickCount.toString(10);var isReady:Boolean = isJavaScriptReady();if (isReady) {//done with TimerTimer(aEvent.target).stop();}}protected function getID():String {return "no override";}//method}//class}//package//// Copyright 2008 John Hicks, Cerium Component Software Inc.//// Available under the terms of the Creative Commons Attribution-Share Alike 3.0 Unported license:// http://creativecommons.org/licenses/by-sa/3.0///// need not find TextField by traversing Display List containers with our runtime value;// instead teach TextField, however deeply hidden, to reach out for its own runtime value;//// JavaScript in Html://// <script type="text/javascript">// var jsReady = false;// var arrValues = new Array();// arrValues['Pic_Taste'] = "<img src=’TextField_Pic.jpg’ width=’95′ height=’69′>";// arrValues['Text_Taste'] = "<br><p align=’right’>\"<i>So what?</i>\" you say?</p>"’// function isReady() {// return jsReady;// }// function pageInit() {// jsReady = true;// }// function getValue(aID) {// return arrValues[aID];// }// </script>// </head>// <body bgcolor="#000000" onload="pageInit()">