[AS3] Zinc 夾 GAIA, d path 錯曬!

http://www.designdisclosure.com/2009/07/make-mdm-zinc-applications-with-the-gaia-framework-for-adobe-flash/
Solution here~

July 5, 2009 by Alistair Stead in ActionScript, Flash Comments ( 3 )
The Gaia Framework for Adobe Flash is a something I have been using for some time. It is one of the most useful frameworks I have found for use with Flash. It adds a great deal of useful functionality and can generate large swathes of source code that you would otherwise need to write from scratch. However it is a very lightweight and unobtrusive ActionScript framework, you are still free to build your flash application in the same way you always have. You can continue to use your existing workflow but also take advantage of the framework features such as scaffolding, swfAddress, SEO optimization and asset loading and pre-loading.

With all this said when using the Gaia Framework with MDM Zinc application wrapper you have a couple of issues to resolve.

File paths and references from the application executable.
External Interface calls.
Most flash developers are likely unfamiliar with developing installable applications so these issues may be daunting when first encountered. However once explained there are simple solutions to both these issues that I will explain in detail.

File Paths and References

When Flash is used within a website it is served by a webserver and files can always be referenced from the root of the website e.g. /flv/example-video.flv or relative to the location of the .swf e.g. flv/example-video.flv.

However when your .swf is running inside the MDM Zinc wrapper it can no longer reliably reference files using the absolute path from root or a relative path. To resolve this you will need to evaluate the location that the application is running at before creating a path to the files based on this computed path.

Zinc Context Object

To solve this problem I have created a singleton context object. This object allows me to run the required application setup code and access the application properties from everywhere within my Gaia based application.

package com.designdisclosure.utils.mdm
{
import mdm.*;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.display.*;

public class ZincContext extends Sprite
{
private static var _instance:ZincContext;
public var zincGlobals:ZincGlobals;

/**
* Public static method to instantiate the ZincContext object.
* @return ZincContext Object Instance
*/
public static function get instance():ZincContext
{
if(_instance === null)
{
_instance = new ZincContext(new SingletonEnforcer());
}

return _instance;
}

public function ZincContext(pvt:SingletonEnforcer):void
{
if(pvt == null)
{
throw new Error(“Error: Instantiation failed: Use ZincContext.instance instead of new.”);
}

// Initialize an MDMZinc application and create a callback method for when this is completed
mdm.Application.init(this, onMDMinit);
}

private function onMDMinit():void
{
// Use this method to setup your Zinc application
// This is the same as applicationDidFinishLaunching from cocoa
}

/**
* Get the absolute path to the application and its files.
* I use a VMWare machine during testing and found
* that the application path is alittle un-reliable
* and needs modification when running in the VM.
*/
public function getApplicationPath():String
{
var path:String = mdm.Application.path;

return path;
}

public function get isZincApplication():Boolean
{
return (getApplicationPath.length)? true : false;
}
}
}

internal class SingletonEnforcer {}
This object can be used to obtain the application path, an absolute location at which the Zinc application is running. The can then be used in any function the loads external resources. However here is the main problem within the Gaia framework. It is possible to update the path to the site.xml file that is loaded in main.as but even if you load the file from an absolute path the framework does not update the paths used to load any assets referenced in the xml file its self.

Fixing the file paths

Although my next suggestion is not by any means ideal as it involves modifying the framework, I have yet to find a better solution. This obviously will lead to problems when updating the framework to future revisions. However these changes re fairly limited in scope as it is only a single file SiteModel.as. Here are my modifications that inject the application path if it exists into any location where external files are loaded.

public function load(path:String):void
{
if (path == null) path = “site.xml”;
if (path != “xml/site.xml” && path != “site.xml”) GaiaDebug.log(“site.xml path = ” + path);
var request:URLRequest = new URLRequest(CacheBuster.create(ZincContext.instance.getApplicationPath()+path));
loader = new URLLoader();
loader.addEventListener(Event.COMPLETE, onLoadComplete);
loader.load(request);
}
private function parseSite():void
{
_title = _xml.@title || “”;
_preloader = _xml.@preloader || ZincContext.instance.getApplicationPath()+”preload.swf”;
_menu = (_xml.@menu == “true”);
_delimiter = _xml.@delimiter || “: “;
_routing = !(_xml.@routing == “false”);
_history = !(_xml.@history == “false”);
_indexFirst = (_xml.@indexFirst == “true”);
_assetPath = _xml.@assetPath || ZincContext.instance.getApplicationPath();
// preloaderDepth
var depth:String = String(_xml.@preloaderDepth).toLowerCase();
if (depth == Gaia.MIDDLE || depth == Gaia.BOTTOM) _preloaderDepth = depth;
else _preloaderDepth = Gaia.TOP;
// preloaderDomain
var domain:String = String(_xml.@preloaderDomain).toLowerCase();
if (domain == Gaia.DOMAIN_CURRENT || domain == Gaia.DOMAIN_NEW) _preloaderDomain = domain;
else _preloaderDomain = Gaia.DOMAIN_NULL;
// defaultFlow
var flow:String = String(_xml.@flow).toLowerCase();
if (flow == Gaia.PRELOAD || flow == Gaia.REVERSE || flow == Gaia.CROSS) _defaultFlow = flow;
else _defaultFlow = Gaia.NORMAL;
if (_routing) _routes = {};
}
private function parsePage(node:XML, parent:PageAsset = null):PageAsset
{
validateNode(node, true);
var isIndex:Boolean = (node.@id == _indexID);
var page:PageAsset = new PageAsset();
page.node = node;
page.id = node.@id;
page.src = ZincContext.instance.getApplicationPath()+node.@src;
page.title = node.@title;
page.bytes = node.@bytes;
page.assetPath = ZincContext.instance.getApplicationPath()+node.@assetPath || _assetPath;

page.preloadAsset = true;
page.menu = (node.@menu == “true”);
if (page.menu && page.title.toLowerCase() == “about”) GaiaDebug.warn(‘* Warning * “About” is not permitted in Flash context menus’);
if (page.menu && page.title.length > 0) _menuArray.push(page);
page.landing = (node.@landing == “true”);
// domain
var domain:String = String(node.@domain).toLowerCase();
if (domain == Gaia.DOMAIN_NEW || domain == Gaia.DOMAIN_CURRENT) page.domain = domain;
// depth
var depth:String = String(node.@depth).toLowerCase();
if (!isIndex)
{
page.setParent(parent);
page.external = (node.@src.split(“.”).pop() != “swf” || node.@src.indexOf(“javascript”) > -1);
if (page.external) page.window = node.@window || “_self”;
if (depth == Gaia.TOP || depth == Gaia.BOTTOM || depth == Gaia.NESTED) page.depth = depth;
else page.depth = Gaia.MIDDLE;
}
else
{
if (depth == Gaia.TOP || depth == Gaia.MIDDLE) page.depth = depth;
else page.depth = Gaia.BOTTOM;
}
// flow
var flow:String = String(node.@flow).toLowerCase();
if (flow == Gaia.NORMAL || flow == Gaia.PRELOAD || flow == Gaia.REVERSE || flow == Gaia.CROSS) page.flow = flow;
// assets
if (node.asset.length() > 0 || node.@seo != undefined) page.assets = parseAssets(node.asset, page, node.@seo, int(node.@seoBytes));
// child pages
if (node.page.length() > 0)
{
page.defaultChild = node.@defaultChild;
page.children = parseChildren(page, node.page);
if (!page.children.hasOwnProperty(page.defaultChild)) page.defaultChild = node.page[0].@id;
}
// terminal page
else
{
if (page.src.substr(page.src.length – 4) == “.swf”) page.landing = true;
if (isIndex) GaiaSWFAddress.isSinglePage = true;
}
// only add terminal and landing pages to routes
if (_routing && page.landing)
{
var route:String = node.@route || page.title;
if (isIndex) route = route || page.id;
page.route = getValidRoute(route, page.id).toLowerCase();
_routes[page.route] = page.branch;
}
return page;
}
These changes allow you to test your application within the flash authoring environment as the application path will be empty and it will load the files with local references. If you run the application inside a Zinc wrapper the files will be references with an absolute path.

External Interface calls

In order for flash applications to communicate with the browser they use External Interface. This is used extensively within the Gaia framework for logging and also search engine optimization with swfAddress. However MDM Zinc does not handle these calls particularly gracefully and as it is not possible to turn this functionality off within the framework we again need to make a couple of modifications.

Blocking external calls

Inside SWFAdress.as there is a test for ExternalInterface.available however inside Zinc this will incorrectly report true. The only fix for this is as follows:

private static var _availability:Boolean = false; // used to be ExternalInterface.available;
Conclusion

The solutions I have proposed here will resolve the issues the prevent you from using Gaia within a Zinc application. However I believe that these issues could be resolved in a number of ways within the respective products.

MDM Zinc need resolve the issue with ExternalInterface and the its incorrect implementation as this is essentially a bug in the platform.

The issues with the Gaia framework can not be considered bugs as the framework does exactly what it was intended to do. However I would suggest that the framework would be greatly improved if additional configuration were made available to users. Being able to turn off the SEO modules such as SWFAddress and external calls would be very helpful. Not only for use within MDM Zinc but also in situations where the flash application is not the entire web page. If you use Gaia to be build a smaller component within a page the SWFAddress calls can also cause problems.

The loading of assets would also be greatly improved if it were possible to update their paths at run time. Obviously this is not a common requirement but it would make the framework much more flexible and far more useful.