For the record, I'm using Flex builder 3.0.2 on Vista with Turbogears 2.02.
Modifying the Auto-generated HTML Wrapper
Let's go ahead and take a look at the HTML wrapper generated by Flex first. Here are the things you're going to change:
- Move inline Javascript into external files. This is the huge kick in the crotch syntactically. Genshi on TG2 hates inline Javascript (for example, you'll have to replace any "<" with "<" for it to pass through Genshi), so it's a good idea to take all that inline code and move it into an external file. Note that though there are multiple sections of inline JS code, I seem to be able to drop them all into a single file (called FlexDetect.js) which I've included below. I haven't fully tested the implications of this, though, so feel free to test and comment.
- Replace <embed> tag with a nested <object> tag.The default wrapper uses and embed tag which, for reasons enumerated in the O'Reily Flex book is bad form. Furthermore, in conjunction with the <noscript> tag (see below), this prevented my .swf from loading in non-IE browsers (e.g. Chrome, Firefox and Safari).
- Remove <noscript> tag. The primary purpose of the <noscript> tag in the generated wrapper is to allow for alternative text for search engine bots (i.e. Google), but, as the O'Reily post notes, the nested <object> tags also allow for this in a more standards-compliant fashion.
Take a look at the before/after template comparison:
Before (Auto-generated by Flex):
<!-- saved from url=(0014)about:internet -->
<html lang="en">
<!--
Smart developers always View Source.
This application was built using Adobe Flex, an open source framework
for building rich Internet applications that get delivered via the
Flash Player or to desktops via Adobe AIR.
Learn more about Flex at http://flex.org
// -->
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<!-- BEGIN Browser History required section -->
<link rel="stylesheet" type="text/css" href="history/history.css" />
<!-- END Browser History required section -->
<title></title>
<script src="AC_OETags.js" language="javascript"></script>
<!-- BEGIN Browser History required section -->
<script src="history/history.js" language="javascript"></script>
<!-- END Browser History required section -->
<style>
body { margin: 0px; overflow:hidden }
</style>
<script language="JavaScript" type="text/javascript">
<!--
// -----------------------------------------------------------------------------
// Globals
// Major version of Flash required
var requiredMajorVersion = 10;
// Minor version of Flash required
var requiredMinorVersion = 0;
// Minor version of Flash required
var requiredRevision = 0;
// -----------------------------------------------------------------------------
// -->
</script>
</head>
<body scroll="no">
<script language="JavaScript" type="text/javascript">
<!--
// Version check for the Flash Player that has the ability to start Player Product Install (6.0r65)
var hasProductInstall = DetectFlashVer(6, 0, 65);
// Version check based upon the values defined in globals
var hasRequestedVersion = DetectFlashVer(requiredMajorVersion, requiredMinorVersion, requiredRevision);
if ( hasProductInstall && !hasRequestedVersion ) {
// DO NOT MODIFY THE FOLLOWING FOUR LINES
// Location visited after installation is complete if installation is required
var MMPlayerType = (isIE == true) ? "ActiveX" : "PlugIn";
var MMredirectURL = window.location;
document.title = document.title.slice(0, 47) + " - Flash Player Installation";
var MMdoctitle = document.title;
AC_FL_RunContent(
"src", "playerProductInstall",
"FlashVars", "MMredirectURL="+MMredirectURL+'&MMplayerType='+MMPlayerType+'&MMdoctitle='+MMdoctitle+"",
"width", "1010",
"height", "650",
"align", "middle",
"id", "MyApp",
"quality", "high",
"bgcolor", "#869ca7",
"name", "MyApp",
"allowScriptAccess","sameDomain",
"type", "application/x-shockwave-flash",
"pluginspage", "http://www.adobe.com/go/getflashplayer"
);
} else if (hasRequestedVersion) {
// if we've detected an acceptable version
// embed the Flash Content SWF when all tests are passed
AC_FL_RunContent(
"src", "MyApp",
"width", "1010",
"height", "650",
"align", "middle",
"id", "MyApp",
"quality", "high",
"bgcolor", "#869ca7",
"name", "MyApp",
"allowScriptAccess","sameDomain",
"type", "application/x-shockwave-flash",
"pluginspage", "http://www.adobe.com/go/getflashplayer"
);
} else { // flash is too old or we can't detect the plugin
var alternateContent = 'Alternate HTML content should be placed here. '
+ 'This content requires the Adobe Flash Player. '
+ '<a href=http://www.adobe.com/go/getflash/>Get Flash</a>';
document.write(alternateContent); // insert non-flash content
}
// -->
</script>
<noscript>
<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
id="MyApp" width="1010" height="650"
codebase="http://fpdownload.macromedia.com/get/flashplayer/current/swflash.cab">
<param name="movie" value="MyApp.swf" />
<param name="quality" value="high" />
<param name="bgcolor" value="#869ca7" />
<param name="allowScriptAccess" value="sameDomain" />
<embed src="MyApp.swf" quality="high" bgcolor="#869ca7"
width="1010" height="650" name="MyApp" align="middle"
play="true"
loop="false"
quality="high"
allowScriptAccess="sameDomain"
type="application/x-shockwave-flash"
pluginspage="http://www.adobe.com/go/getflashplayer">
</embed>
</object>
</noscript>
</body>
</html>
After (Modified for use with Turbogears 2)
Note here that I have added a div and CSS to center the .swf within the HTML frame. Also note that references to external files have been wrapped in tg.url{}calls.
<!-- saved from url=(0014)about:internet -->
<html lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
<!--
Smart developers always View Source.
This application was built using Adobe Flex, an open source framework
for building rich Internet applications that get delivered via the
Flash Player or to desktops via Adobe AIR.
Learn more about Flex at http://flex.org
// -->
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<!-- BEGIN Browser History required section -->
<link rel="stylesheet" type="text/css" href="{tg.url('/flex/history/history.css')}" />
<!-- END Browser History required section -->
<title>Restaurant Recommendations by Yourcoolfriend.com</title>
<script src="{tg.url('/flex/AC_OETags.js')}" language="javascript"></script>
<!-- BEGIN Browser History required section -->
<script src="{tg.url('/flex/history/history.js)}" language="javascript"></script>
<!-- END Browser History required section -->
<style>
body { margin: 0px auto;
overflow:hidden;
background-color: black; }
.centered {
margin: auto;
width: 1010;
height: 650;
}
</style>
<script src="{tg.url('/flex/FlexDetectGlobals.js)}" language="javascript"></script>
</head>
<body scroll="no">
<div id="container" class="centered">
<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
id="MyApp" width="1010" height="650"
codebase="http://fpdownload.macromedia.com/get/flashplayer/current/swflash.cab"
class="centered">
<param name="movie" value="${tg.url('/flex/MyApp.swf')}" />
<param name="quality" value="high" />
<param name="bgcolor" value="#869ca7" />
<param name="allowScriptAccess" value="sameDomain" />
<!--[if !IE]>-->
<object id="MyApp" width="1010" height="650"
codebase="http://fpdownload.macromedia.com/get/flashplayer/current/swflash.cab"
data="${tg.url('/flex/MyApp.swf')}"
class="centered">
<!--[endif]-->
<h1>Personalized Restaurant Recommendations by Yourcoolfriend.com</h1>
<!--[if !IE]>-->
</object>
<!--[endif]-->
</object>
</div>
</body>
</html>
Getting Flex Files into TG2
Now let's deal with the TG2 directory structure (it wasn't clear to me at first where to put my .swf, template file, and associated Flex history javascript so that TG2/Pylons can serve it properly). I'm printing my directory structure below. Note that the .swf and JS files go under the public directory within the TG2 app directory. This is because, by default, Pylons is configured to recognize files in public as static, servable files. The main HTML wrapper for the .swf goes under templates/. I chose to create flex subfolder for clarity, though this isn't necessary. The ellipses indicate are simply shorthand for skipping past files irrelevant to this tutorial.
tg2top/
development.ini
...
mytg2app/
config/
controllers/
...
public/
flex/
AC_OETags.js
MyApp.swf
FlexDetect.js
playerProductInstall.swf
history/
history.css
history.js
historyFrame.html
templates/
master.html
...
flex/
__init__.py
MyApp.html
FlexDetect.js
This is the new external JS file compiling all the inline JS.
// Globals
// Major version of Flash required
var requiredMajorVersion = 10;
// Minor version of Flash required
var requiredMinorVersion = 0;
// Minor version of Flash required
var requiredRevision = 0;
//Version check for the Flash Player that has the ability to start Player Product Install (6.0r65)
var hasProductInstall = DetectFlashVer(6, 0, 65);
// Version check based upon the values defined in globals
var hasRequestedVersion = DetectFlashVer(requiredMajorVersion, requiredMinorVersion, requiredRevision);
if ( hasProductInstall && !hasRequestedVersion ) {
// DO NOT MODIFY THE FOLLOWING FOUR LINES
// Location visited after installation is complete if installation is required
var MMPlayerType = (isIE == true) ? "ActiveX" : "PlugIn";
var MMredirectURL = window.location;
document.title = document.title.slice(0, 47) + " - Flash Player Installation";
var MMdoctitle = document.title;
AC_FL_RunContent(
"src", "playerProductInstall",
"FlashVars", "MMredirectURL="+MMredirectURL+'&MMplayerType='+MMPlayerType+'&MMdoctitle='+MMdoctitle+"",
"width", "1010",
"height", "650",
"align", "middle",
"id", "DescribeDetails",
"quality", "high",
"bgcolor", "#869ca7",
"name", "DescribeDetails",
"allowScriptAccess","sameDomain",
"type", "application/x-shockwave-flash",
"pluginspage", "http://www.adobe.com/go/getflashplayer"
);
} else if (hasRequestedVersion) {
// if we've detected an acceptable version
// embed the Flash Content SWF when all tests are passed
AC_FL_RunContent(
"src", "DescribeDetails",
"width", "1010",
"height", "650",
"align", "middle",
"id", "DescribeDetails",
"quality", "high",
"bgcolor", "#869ca7",
"name", "DescribeDetails",
"allowScriptAccess","sameDomain",
"type", "application/x-shockwave-flash",
"pluginspage", "http://www.adobe.com/go/getflashplayer"
);
} else { // flash is too old or we can't detect the plugin
var alternateContent = 'Alternate HTML content should be placed here. '
+ 'This content requires the Adobe Flash Player. '
+ 'Get Flash';
document.write(alternateContent); // insert non-flash content
}
Turbogears 2 Controller Code
Finally, let's add the controller code within TG2 to tie it all together.
class FlexController(BaseController):
"""Serves MyApp.swf"""
@expose('MyTg2App.templates.flex.MyApp')
def index(self):
return {}