iterators, small fix and question on silent error
  • madmattmadmatt March 2012

    Hi, first message, and first thank you for producing such a tool.

    I have been experimenting with iterators and , if I understand the docs, I should be able to run this script

     

    xjsfl.init(this);
    clear();

    try {


    var doc = fl.openDocument("file:////mypath/myfile.fla");

    Iterators.items(true, null, processLayer, processFrame, processElement);
    doc.close(false);
    }
    catch (err) {
    xjsfl.debug.error(err);
    }
    function processLayer(layer, index, layers, context) {
    trace('> ' + layer.name);

    }

    function processFrame(frame, index, frames, context) {
    trace(' > ' + frame.startFrame);

    }

    function processElement(element, index, elements, context) {
    trace(' > ' + element.name);
    }


    in order to iterate through all the library elements of my source file.

     

    First off, I had to fix what I believe is a bug in core/jsfl/libraries/flash/Context.jsfl:

    diff --git a/core/jsfl/libraries/flash/Context.jsfl b/core/jsfl/libraries/flash/Context.jsfl
    index e24989b..8da2662 100644
    --- a/core/jsfl/libraries/flash/Context.jsfl
    +++ b/core/jsfl/libraries/flash/Context.jsfl
    @@ -61,15 +61,17 @@
    if(timeline)this.setTimeline(timeline);
    if(this.timeline)
    {
    - if(layer)this.setLayer(layer);
    - if(this.layer.layerType !== 'folder')
    - {
    - this.setFrame(frame);
    - if(this.frame)
    + if(layer){
    + this.setLayer(layer);
    + if(this.layer.layerType !== 'folder')
    {
    - if(element)this.setElement(element);
    + this.setFrame(frame);
    + if(this.frame)
    + {
    + if(element)this.setElement(element);
    + }
    }
    - }
    + }
    }
    }
    return this;

     

    because I was getting errors when this.layer was undefined and layer.layerType was tested.

     

    With this small fix I am able to (sort of) run the Iterators.items code but I always get stuck with what I believe you guys call a silent error on the console:

    The following JavaScript error(s) occurred:

     

    that is, no exception raised, the code stops executing somewhere inside the iterator (doc.close(false) doesn't get called, nor the catch block), no debug info.

    This can happen, depending on the file, right away or halfway through the library scan.

    I can successully use the layers method on the same files with no issues.

    At the moment I have reverted to using the old (and not so cool) library scan nested for loop found in various sample jsfl files, but it would be great if I could understand whether I'm doing something I'm not supposed to do, or whether there's a problem and I can help fix it.

    Unfortunately, with no debug info and no exception raised I cannot really tell what's happening ...

  • DaveDave March 2012

    Hey Matt,

    Doh! Iterators is one of the classes that has a couple of to-do items attached to it.

    It was one of the earlier classes written, and in the time between then and now, I've been noting down bugs to fix. I'll try and take a look at this in the next week or so - unfortunately I'm really behind with "real" work at the moment, so I need to invest some time in that too.

    If you can make do with the old way or your fix in the meantime, I'll let you know via the forum when I've had a chance to look at it.

    I appreciate your detailed post; it will be helpful for sure.

    Cheers,

    Dave

  • DaveDave March 2012

    One last thing - CS5 doesn't seem to like outputting error messages (wtf Adobe!?) so if you're on CS5, run the same code in CS4 and see what comes up.

  • madmattmadmatt March 2012

    Hi Dave, thanks, I am indeed using CS5.5, will try CS 4 (If I can find a copy somewhere) and will let you know.

    I can make do with the 'traditional' library traversal algorithm, no worries.

    Now on to tackling file mod times and generating dependency matrixes (I'm trying to integrate .fla publishing in a continuous build environment, so far it looks like I have all the dots worked out, I only need to connect them in the right way :) 

  • DaveDave March 2012

    Yup, you should be able to just create a new File, then test its modifiedDate attribute:

    var file = new File('path/to/file');
    trace(file.modified);
    trace(file.modifiedDate);

    If you need to grab files from a folder you can use Utils.walkFolder(), or the new Utils.glob() function:

    var uris = Utils.glob('path/to/folder/', '*.jsfl'); // all jsfl files in that folder
    var uris = Utils.glob('path/to/folder/', '**/*.jsfl'); // all jsfl files recursively
    var uris = Utils.glob('path/to/folder/', '**/*.*'); // all files recursively
    // etc...

    I need to do some more testing, but I think it's there.

    Enjoy :)

  • madmattmadmatt March 2012

    I am indeed enjoing working with this framework instead of having to (re)write thousands of lines of potentially buggy code.

    This is an example of what I have achieved in half a day worth of hacking while being constantly nagged by coworkers ;)

    This is the source code for a script that opens a target fla, checks its imports statement for included classes, parses them and builds a dependency list, while collecting modification times, and then decides whether the fla needs publishing because some of the dependencies have been changed:

    xjsfl.init(this);
    clear();

    try {
    var root = "file:////rootpath";
    var workFile = "myfla.fla";
    var asPath = 'file:////sourcepath';
    var fla = new Fla(workFile, root, asPath);
    trace('Dependency List:');
    for (var l in fla.depList) {
    trace(l);
    }
    if (fla.needsPublish) {
    trace('Some mods to source files ( ' + fla.dirtyListString + ' ), compile needed !');
    }
    else{
    trace('No mods to source files, no compile needed !');
    }
    fla.close();
    }
    catch (err) {
    xjsfl.debug.error(err);
    }

    and this is the output:

    Dependency List:
    com/eg/util/core/Multigame.as
    com/eg/util/components/GameBottomBar.as
    com/eg/util/balance/Balance.as
    com/eg/util/Constant.as
    com/eg/util/core/Game.as
    com/eg/util/Element.as
    com/eg/util/layout/Frame.as
    com/eg/util/components/ProfileSelector.as
    com/eg/util/Util.as
    com/eg/util/jackpot/JackpotManager.as
    com/eg/util/layout/LayoutManager.as
    com/eg/util/Messages.as
    com/eg/util/core/Config.as
    com/eg/util/MyLoader.as
    com/eg/util/Translate.as
    com/eg/util/UserData.as
    com/eg/util/components/GameMenu.as
    com/eg/util/balance/BalanceManager.as
    com/eg/game/cd/lotto/Config.as
    com/eg/util/logger/Console.as
    com/eg/util/components/ActiveGameMenu.as
    com/eg/util/login/Login.as
    com/eg/util/sound/SSound.as
    com/eg/util/components/Button.as
    com/eg/util/components/GenericButton.as
    com/eg/util/components/IntroLoader.as
    com/eg/util/components/SuperTextField.as
    Some mods to source files ( - file://///components/com/eg/util/core/Multigame.as - file://///components/com/eg/util/MyLoader.as ), compile needed !

     

    I have created a couple of library classes that do the real work, and am planning to use this to be able to run various ant tasks that, depending on the need, calculate the dependencies, republish modified files with custom publish settings (NO TRACES and don't trust the developers to do it!) and, eventually, update locale files with new text strings/delete old ones (lazy developers always 'forget' to update locales by hand).

    Once this is done we will be (at last) able to implement a continuous build system that is really useful

  • DaveDave March 2012

    Funny you should mention Publish settings, I have an OO PublishSettings class waiting to be deployed! It's essentially an API around document.export/importPublishSettingsString() and with Komodo, obviously you get auto-complete, so you will be able to do stuff like this easily:

    var settings = new PublishSettings(this);
    settings.as.documentClass = 'blah'; 

    A few tips:

    You can use inspect() to dump out objects hierarchically:

    inspect(fla.depList, 'Dependency list');

    You can use format() instead of string concatenation, which keeps your code visually cleaner:

    format('Some mods to source files ({str}), compile needed !', fla.dirtyListString);

    // or even

    format('Some mods to source files ({dirtyListString}), compile needed !', fla);
    Debug also has a global shortcut:
    debug(err);
    Nice OO Fla lib there :)
  • madmattmadmatt April 2012

    Just for the sake of the discussion (and to show off a little bit) this is the final script I have come up with.

    The script scans recursively through a given project directory (thanks Utils.glob) and, for each directory, opens all the FLA files, scans for dependencies , generates an ant build file (thanks Template class) . The build file is saved in a separate build folder that mirrors the project folder structure, using the Template class it is really easy to automatically generate correct build files that can be run throgh ant.

     

     

    xjsfl.init(this);
    clear();
    
    try {
        var root = "file:////mypath/";
        xjsfl.settings.folders.set('templateFla', root+'build/flash/templates/');
        var asPath = 'file:////mysrcpath/as/components/';
        var buildPath = root+'mybuilddir'
    var srcPath = root+'default/' var workDirs=Utils.glob(root+"default/",'**/'); for (var k in workDirs){ if (! workDirs[k].match(/\.svn/)){ var workFiles = Utils.glob(workDirs[k],'*.fla'); if(workFiles.length > 0){ var localPath = workDirs[k].substr(workDirs[k].indexOf("/default/")+1); var f = new Folder(buildPath + localPath,true); var buildFile = new Template('{templateFla}/build-fla.tpl', {dirName : localPath}); var targetCompileList = ''; var targetBuildList = ''; for (var flaFile in workFiles){ var fla = new Fla(workFiles[flaFile].substr(workFiles[flaFile].indexOf(root) + root.length), root, asPath); var nodeFile = new Template('{templateFla}/node-fla.tpl',{srcFile:fla.getSourceFileName, targetFile:fla.getPublishFileName}); if (flaFile > 0){ targetCompileList = targetCompileList + ','; targetBuildList = targetBuildList + ','; } targetCompileList = targetCompileList + 'compile.'+fla.getFileName; targetBuildList = targetBuildList + 'build.'+fla.getFileName; nodeFile.set('name',fla.getFileName); for (var l in fla.depListAnt) { nodeFile.set('depItem','\t\t\t'+fla.depListAnt[l],true); } buildFile.set('targetGame',nodeFile.render(),true); fla.close(); } buildFile.set('targetCompileList',targetCompileList); buildFile.set('targetBuildList',targetBuildList); var antFile=new File(buildPath+localPath+'build.xml', buildFile.render()).save(); } } } } catch (err) { xjsfl.debug.error(err); }

     

    This is the main template file:

    <project name="{dirName}" basedir=".">
         <property name="project.dtd" location="{dirName}"/>
         <echo message="Working on: ${project.dtd}"/>
        {targetGame}
    
        <target name="compile" depends="{targetCompileList}">
    
        </target>
        <target name="build" depends="{targetBuildList}">
    
        </target>
    
    </project>
    
    and this is a single item  template (that gets substituted to the targetgame placeholder, one entry per .fla file in a given dir:
        <target name="checkforchanges.{name}">
            <uptodate property="nochanges.{name}" targetfile="{targetFile}">
                {depItem}
            </uptodate>
        </target>
    
        <target name="compile.{name}" depends="checkforchanges.{name}" unless="nochanges.{name}">
            <antcall target="build.{name}"/>
        </target>
        <target name="build.{name}">
            <trycatch property="foo" reference="bar">
                <try>
                    <buildFla flash.version="${flash.version}" src.file="{srcFile}"
                              dst.file="{targetFile}"/>
                </try>
                <catch>
                    <delete file="{targetFile}"/>
                    <fail/>
                </catch>
            </trycatch>
        </target>
    
    

     

    and this is the main build.xml file where generic properties and macros are defined:

     

    <project name="myprj" basedir=".">
        <property name="base.dir" value="/mypath"/>
        <taskdef resource="net/sf/antcontrib/antlib.xml">
            <classpath>
                <pathelement location="${base.dir}/build/lib/ant-contrib-1.0b3.jar"/>
            </classpath>
        </taskdef>
        <taskdef resource="FuelAntTasks.properties" classpath="${base.dir}/build/lib/FuelAntTasks.jar"/>
        <macrodef name="buildFla">
            <attribute name="src.file"/>
            <attribute name="dst.file"/>
            <attribute name="flash.version"/>
            <sequential>
                <flash version="@{flash.version}" closeDocs="true" quit="false" timeout="100000" >
                    <fla file="@{src.file}" build="publish" swf="@{dst.file}"/>
                </flash>
    
            </sequential>
        </macrodef>
    
    
        <property name="dev.dir" value="${base.dir}/build"/>
        <property name="temp.dir" value="${base.dir}/build"/>
        <property name="source.dir" value="${base.dir}/as/components"/>
        <property name="fla.dir" value="${base.dir}/default"/>
    
        <property name="flash.version" value="CS55"/>
    
        <target name="compile">
    
            <for param="file" keepgoing="true" >
                <path>
                    <fileset dir="./mybuilddir/">
                        <include name="**/build.xml"/>
                    </fileset>
                </path>
                <sequential>
                    <ant antfile="@{file}" target="compile" inheritall="true" inheritrefs="true" />
                </sequential>
            </for>
        </target>
    
    
    </project>

     

    Thanks to xJSFL I was able to achieve this in a (relatively) short time, I can't imagine how much it would have taken me if I had to write all the accessory function by myself .... 

     

     

    Right now I am already working on expanding the script capability in order to be able to generate also an ant target that will go looking for missing fonts and missing/updated locale strings ... and I am really interested in the profile class, right now it's plain and old search and replace.

     

    Again, thanks Dave.

     

    PS:

    If anyone is interested I am using this additional ant task (http://code.google.com/p/fuelanttasks/) to be able to launch CS5 and do the publishing from an ant script

     

  • DaveDave April 2012

    Nice one Matt!

    This is what it's all about, and yes, Template is the bomb. All the xJSFL API docs are generated using Template, as is the stack trace when xJSFL debugs an error.

    Great to see you nesting Templates as well!

    You might next want to look at:

    1. Adding a UI using XUL.create()
    2. Packaging it as a module.

    And if whilst using the code there's anything you think could be improved on, drop a comment in the docs, or a post here.

    Glad it's working out for you! I'd have hate to have written this framework and then have had no one use it ;)

  • madmattmadmatt April 2012

    I think I'll write some more use cases and then think of thebest way to package it, as it is I am already thinking of splitting what I have written in at least two parts:

    - Modules that can be used through a UI 

    - Modules used to run in background for the build process

     

    I really can't understand how people is can manage complex projects without the assistance of external tools like this when flash is involved.

    For us using flash/flex builder to code and flash for the animations/graphics isn't really an option given the kind of animations/the complexity of the processes it would require and I have refused outright to adopt one of the many so called 'workflows' for handling changes inside fla files that involve writing down by hand the changes and/or the steps to perform so that someone else can maintain the change documents.

     

    I guess this is a typical case where every sane person is forced to create his/her own tools , unfortunately

  • DaveDave April 2012

    Maybe. Writing tools is pretty fun though. The sense of satisfaction is immense :)

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Sign In with OpenID Sign In with Google Sign In with Twitter

Sign In Apply for Membership

Tagged