package {
import com.gerantech.extensions.iab.Purchase;
import core.Assets;
import core.BillingManager;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import objects.Car;
import objects.GasGauge;

[SWF(width=720, height=1280, frameRate=60, backgroundColor=0x000000)]
public class TrivialDrive extends Sprite
{
    private var _billingManager:BillingManager;
    private var _car:Car;
    private var _title:Sprite;
    private var _gauge:GasGauge;
    private var _driveBtn:Sprite;
    private var _buyGasBtn:Sprite;
    private var _upgradeBtn:Sprite;
    private var _infiniteGasBtn:Sprite;
    private var _gasLevel:int;
    private var _waitBackground:Sprite;
    private var _waitIcon:Sprite;

    public function TrivialDrive()
{
        stage.color = 0x303030;

        _billingManager = BillingManager.instance;
        _billingManager.addEventListener(BillingManager.ON_INITIALIZED, onInitialized);

        _title = new Sprite();
        _title.addChild(Assets.getBitmap("title"));
        _title.x = (stage.stageWidth - _title.width) * 0.5;
        _title.y = 100;
        addChild(_title);

        _car = new Car();
        _car.x = (stage.stageWidth - _car.width) * 0.5;
        _car.y = 250;
        addChild(_car);

        _gauge = new GasGauge();
        _gauge.x = (stage.stageWidth - _gauge.width) * 0.5;
        _gauge.y = 500;
        addChild(_gauge);

        _driveBtn = new Sprite();
        _driveBtn.addChild(Assets.getBitmap("drive"));
        _driveBtn.x = stage.stageWidth * 0.25 - _driveBtn.width * 0.5;
        _driveBtn.y = 700;
        addChild(_driveBtn);

        _buyGasBtn = new Sprite();
        _buyGasBtn.addChild(Assets.getBitmap("buy_gas"));
        _buyGasBtn.x = stage.stageWidth * 0.75 - _buyGasBtn.width * 0.5;
        _buyGasBtn.y = 700;
        addChild(_buyGasBtn);

        _upgradeBtn = new Sprite();
        _upgradeBtn.addChild(Assets.getBitmap("upgrade_app"));
        _upgradeBtn.x = stage.stageWidth * 0.25 - _upgradeBtn.width * 0.5;
        _upgradeBtn.y = 900;
        addChild(_upgradeBtn);

        _infiniteGasBtn = new Sprite();
        _infiniteGasBtn.addChild(Assets.getBitmap("get_infinite_gas"));
        _infiniteGasBtn.x = stage.stageWidth * 0.75 - _infiniteGasBtn.width * 0.5;
        _infiniteGasBtn.y = 900;
        addChild(_infiniteGasBtn);

        _waitBackground = new Sprite();
        _waitBackground.graphics.beginFill(0x000000, 0.75);
        _waitBackground.graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
        _waitBackground.graphics.endFill();
        addChild(_waitBackground);

        _waitIcon = new Sprite();
        _waitIcon.addChild(Assets.getBitmap("wait"));
        _waitIcon.x = stage.stageWidth * 0.5 - _infiniteGasBtn.width * 0.5;
        _waitIcon.y = stage.stageHeight * 0.5 - _infiniteGasBtn.height * 0.5;
        addChild(_waitIcon);
    }

    protected function onInitialized(event:Event):void {
        _billingManager.removeEventListener(BillingManager.ON_INITIALIZED, onInitialized);

        // Add event listeners to buttons
        _buyGasBtn.addEventListener(MouseEvent.CLICK, buyGas);
        _driveBtn.addEventListener(MouseEvent.CLICK, drive);
        _upgradeBtn.addEventListener(MouseEvent.CLICK, upgrade);
        _infiniteGasBtn.addEventListener(MouseEvent.CLICK, subscribe);

        // Check if client has bought the premium car (permanent product)
        var premiumCar:Purchase = _billingManager.getPurchase("my.product.id2");
        if( premiumCar ) {
            trace("premiumCar >", premiumCar, premiumCar.itemType, premiumCar.packageName, premiumCar.sku, premiumCar.token, premiumCar.developerPayload);
            _car.setToPremium();
        }

        // Check if client has infinite gas (subscribed)
        var infiniteGas:Purchase = _billingManager.getPurchase("my.product.id3");
        if( infiniteGas ) {
            trace("infiniteGas >", infiniteGas, infiniteGas.itemType, infiniteGas.packageName, infiniteGas.sku, infiniteGas.token, infiniteGas.developerPayload);
            _gasLevel = -1;
            _gauge.setTo(_gasLevel);
        }

        // Remove wait assets
        removeChild(_waitBackground);
        removeChild(_waitIcon);
    }

    protected function buyGas(event:MouseEvent):void {
        _billingManager.purchase("my.product.id1", true, "payload_for_gas");
        _billingManager.addEventListener(BillingManager.ON_PURCHASE_SUCCESS, onPurchaseSuccess);
    }

    protected function onPurchaseSuccess(event:Event):void {
        _billingManager.removeEventListener(BillingManager.ON_PURCHASE_SUCCESS, onPurchaseSuccess);
        if (_gasLevel >= 0) // _gasLevel = -1 means infinite gas (subscribed user)
            _gasLevel++;
        _gauge.setTo(_gasLevel);
    }

    protected function drive(event:MouseEvent):void {
        if (_gasLevel > 0)
            _gasLevel--;
        _gauge.setTo(_gasLevel);
    }

    protected function upgrade(event:MouseEvent):void {
        _billingManager.addEventListener(BillingManager.ON_PURCHASE_SUCCESS, onUpgradeSuccess);
        _billingManager.purchase("my.product.id2", false, "payload_for_upgrade");
    }

    protected function onUpgradeSuccess(event:Event):void {
        _billingManager.removeEventListener(BillingManager.ON_PURCHASE_SUCCESS, onUpgradeSuccess);
        _car.setToPremium();
    }

    protected function subscribe(event:MouseEvent):void {
        _billingManager.addEventListener(BillingManager.ON_PURCHASE_SUCCESS, onSubscribtionSuccess);
        _billingManager.subscribe("my.product.id3");
    }

    protected function onSubscribtionSuccess(event:Event):void {
        _billingManager.removeEventListener(BillingManager.ON_PURCHASE_SUCCESS, onSubscribtionSuccess);
        _gasLevel = -1;
        _gauge.setTo(_gasLevel);
    }
}
}

package {
import com.gerantech.extensions.iab.Purchase;
import core.Assets;
import core.BillingManager;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import objects.Car;
import objects.GasGauge;

[SWF(width=720, height=1280, frameRate=60, backgroundColor=0x000000)]
public class TrivialDrive extends Sprite
{
    private var _billingManager:BillingManager;
    private var _car:Car;
    private var _title:Sprite;
    private var _gauge:GasGauge;
    private var _driveBtn:Sprite;
    private var _buyGasBtn:Sprite;
    private var _upgradeBtn:Sprite;
    private var _infiniteGasBtn:Sprite;
    private var _gasLevel:int;
    private var _waitBackground:Sprite;
    private var _waitIcon:Sprite;

    public function TrivialDrive()
{
        stage.color = 0x303030;

        _billingManager = BillingManager.instance;
        _billingManager.addEventListener(BillingManager.ON_INITIALIZED, onInitialized);

        _title = new Sprite();
        _title.addChild(Assets.getBitmap("title"));
        _title.x = (stage.stageWidth - _title.width) * 0.5;
        _title.y = 100;
        addChild(_title);

        _car = new Car();
        _car.x = (stage.stageWidth - _car.width) * 0.5;
        _car.y = 250;
        addChild(_car);

        _gauge = new GasGauge();
        _gauge.x = (stage.stageWidth - _gauge.width) * 0.5;
        _gauge.y = 500;
        addChild(_gauge);

        _driveBtn = new Sprite();
        _driveBtn.addChild(Assets.getBitmap("drive"));
        _driveBtn.x = stage.stageWidth * 0.25 - _driveBtn.width * 0.5;
        _driveBtn.y = 700;
        addChild(_driveBtn);

        _buyGasBtn = new Sprite();
        _buyGasBtn.addChild(Assets.getBitmap("buy_gas"));
        _buyGasBtn.x = stage.stageWidth * 0.75 - _buyGasBtn.width * 0.5;
        _buyGasBtn.y = 700;
        addChild(_buyGasBtn);

        _upgradeBtn = new Sprite();
        _upgradeBtn.addChild(Assets.getBitmap("upgrade_app"));
        _upgradeBtn.x = stage.stageWidth * 0.25 - _upgradeBtn.width * 0.5;
        _upgradeBtn.y = 900;
        addChild(_upgradeBtn);

        _infiniteGasBtn = new Sprite();
        _infiniteGasBtn.addChild(Assets.getBitmap("get_infinite_gas"));
        _infiniteGasBtn.x = stage.stageWidth * 0.75 - _infiniteGasBtn.width * 0.5;
        _infiniteGasBtn.y = 900;
        addChild(_infiniteGasBtn);

        _waitBackground = new Sprite();
        _waitBackground.graphics.beginFill(0x000000, 0.75);
        _waitBackground.graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
        _waitBackground.graphics.endFill();
        addChild(_waitBackground);

        _waitIcon = new Sprite();
        _waitIcon.addChild(Assets.getBitmap("wait"));
        _waitIcon.x = stage.stageWidth * 0.5 - _infiniteGasBtn.width * 0.5;
        _waitIcon.y = stage.stageHeight * 0.5 - _infiniteGasBtn.height * 0.5;
        addChild(_waitIcon);
    }

    protected function onInitialized(event:Event):void {
        _billingManager.removeEventListener(BillingManager.ON_INITIALIZED, onInitialized);

        // Add event listeners to buttons
        _buyGasBtn.addEventListener(MouseEvent.CLICK, buyGas);
        _driveBtn.addEventListener(MouseEvent.CLICK, drive);
        _upgradeBtn.addEventListener(MouseEvent.CLICK, upgrade);
        _infiniteGasBtn.addEventListener(MouseEvent.CLICK, subscribe);

        // Check if client has bought the premium car (permanent product)
        var premiumCar:Purchase = _billingManager.getPurchase("my.product.id2");
        if( premiumCar ) {
            trace("premiumCar >", premiumCar, premiumCar.itemType, premiumCar.packageName, premiumCar.sku, premiumCar.token, premiumCar.developerPayload);
            _car.setToPremium();
        }

        // Check if client has infinite gas (subscribed)
        var infiniteGas:Purchase = _billingManager.getPurchase("my.product.id3");
        if( infiniteGas ) {
            trace("infiniteGas >", infiniteGas, infiniteGas.itemType, infiniteGas.packageName, infiniteGas.sku, infiniteGas.token, infiniteGas.developerPayload);
            _gasLevel = -1;
            _gauge.setTo(_gasLevel);
        }

        // Read gas level from local storage
        var sharedObj:SharedObject = SharedObject.getLocal("saveData");
        _gasLevel = sharedObj.data.gasLevel;
        _gauge.setTo(_gasLevel);

        // Remove wait assets
        removeChild(_waitBackground);
        removeChild(_waitIcon);
    }

    protected function buyGas(event:MouseEvent):void {
        _billingManager.purchase("my.product.id1", true, "payload_for_gas");
        _billingManager.addEventListener(BillingManager.ON_PURCHASE_SUCCESS, onPurchaseSuccess);
    }

    protected function onPurchaseSuccess(event:Event):void {
        _billingManager.removeEventListener(BillingManager.ON_PURCHASE_SUCCESS, onPurchaseSuccess);
        if (_gasLevel >= 0) // _gasLevel = -1 means infinite gas (subscribed user)
            _gasLevel++;
        _gauge.setTo(_gasLevel);

        // save gas level locally
        saveGasLevel();
    }

    protected function drive(event:MouseEvent):void {
        if (_gasLevel > 0)
            _gasLevel--;
        _gauge.setTo(_gasLevel);

        // save gas level locally
        saveGasLevel();
    }

    private function saveGasLevel():void {
        var sharedObj:SharedObject = SharedObject.getLocal("saveData");
        sharedObj.data.gasLevel = _gasLevel;
        sharedObj.flush();
    }

    protected function upgrade(event:MouseEvent):void {
        _billingManager.addEventListener(BillingManager.ON_PURCHASE_SUCCESS, onUpgradeSuccess);
        _billingManager.purchase("my.product.id2", false, "payload_for_upgrade");
    }

    protected function onUpgradeSuccess(event:Event):void {
        _billingManager.removeEventListener(BillingManager.ON_PURCHASE_SUCCESS, onUpgradeSuccess);
        _car.setToPremium();
    }

    protected function subscribe(event:MouseEvent):void {
        _billingManager.addEventListener(BillingManager.ON_PURCHASE_SUCCESS, onSubscribtionSuccess);
        _billingManager.subscribe("my.product.id3");
    }

    protected function onSubscribtionSuccess(event:Event):void {
        _billingManager.removeEventListener(BillingManager.ON_PURCHASE_SUCCESS, onSubscribtionSuccess);
        _gasLevel = -1;
        _gauge.setTo(_gasLevel);
    }
}
}

Update TrivialDrive.as

Update TrivialDrive.as

added local save of gas level


/*
# Copyright (c) 2008-2010 rPath, Inc.
#
# This program is distributed under the terms of the MIT License as found
# in a file called LICENSE. If it is not present, the license
# is always available at http://www.opensource.org/licenses/mit-license.php.
#
# This program is distributed in the hope that it will be useful, but
# without any warranty; without even the implied warranty of merchantability
# or fitness for a particular purpose. See the MIT License for full details.
#
*/

package com.rpath.xobj
{

[Bindable]
public class FilterTerm
{
    public function FilterTerm(name:String,
                               value:String,
                               operator:String = EQUAL)
    {
        super();

        this.name = name;
        this.operator = operator;
        this.value = value;
    }

    /**
     * The field/property to filter on (LHS)
     */
    public var name:String;

    /**
     * The logical operator to apply (see static consts below)
     */
    public var operator:String;

    /**
     * The value to test the field against (RHS)
     */
    public var value:String;

    public static const EQUAL:String = "EQUAL";
    public static const NOT_EQUAL:String = "NOT_EQUAL";

    public static const LESS_THAN:String = "LESS_THAN";
    public static const LESS_THAN_OR_EQUAL:String = "LESS_THAN_OR_EQUAL";
    public static const GREATER_THAN:String = "GREATER_THAN";
    public static const GREATER_THAN_OR_EQUAL:String = "GREATER_THAN_OR_EQUAL";
    public static const LIKE:String = "LIKE";
    public static const NOT_LIKE:String = "NOT_LIKE";
    public static const IN:String = "IN";
    public static const NOT_IN:String = "NOT_IN";
    public static const MATCHING:String = "MATCHING";
    public static const NOT_MATCHING:String = "NOT_MATCHING";

    public static const YES:String = "YES";
    public static const NO:String = "NO";

}
}

/*
# Copyright (c) 2008-2010 rPath, Inc.
#
# This program is distributed under the terms of the MIT License as found
# in a file called LICENSE. If it is not present, the license
# is always available at http://www.opensource.org/licenses/mit-license.php.
#
# This program is distributed in the hope that it will be useful, but
# without any warranty; without even the implied warranty of merchantability
# or fitness for a particular purpose. See the MIT License for full details.
#
*/

package com.rpath.xobj
{

[RemoteClass]
[Bindable]
public class FilterTerm
{
    public function FilterTerm(name:String,
                               value:String,
                               operator:String = EQUAL)
    {
        super();

        this.name = name;
        this.operator = operator;
        this.value = value;
    }

    /**
     * The field/property to filter on (LHS)
     */
    public var name:String;

    /**
     * The logical operator to apply (see static consts below)
     */
    public var operator:String;

    /**
     * The value to test the field against (RHS)
     */
    public var value:String;

    public static const EQUAL:String = "EQUAL";
    public static const NOT_EQUAL:String = "NOT_EQUAL";

    public static const LESS_THAN:String = "LESS_THAN";
    public static const LESS_THAN_OR_EQUAL:String = "LESS_THAN_OR_EQUAL";
    public static const GREATER_THAN:String = "GREATER_THAN";
    public static const GREATER_THAN_OR_EQUAL:String = "GREATER_THAN_OR_EQUAL";
    public static const LIKE:String = "LIKE";
    public static const NOT_LIKE:String = "NOT_LIKE";
    public static const IN:String = "IN";
    public static const NOT_IN:String = "NOT_IN";
    public static const MATCHING:String = "MATCHING";
    public static const NOT_MATCHING:String = "NOT_MATCHING";

    public static const YES:String = "YES";
    public static const NO:String = "NO";

}
}

Make FilterTerm [RemoteClass] to enable clone() to work


/**
* jQuery AS3 Webcam
*
* Copyright (c) 2012, Sergey Shilko (sergey.shilko@gmail.com)
*
* @author Sergey Shilko
* @see https://github.com/sshilko/jQuery-AS3-Webcam
*
**/

/* SWF external interface:
 * webcam.save() - get base64 encoded JPEG image
 * webcam.getCameraList() - get list of available cams
 * webcam.setCamera(i) - set camera, camera index retrieved with getCameraList
 * webcam.getResolution() - get camera resolution actually applied
 * */

/* External triggers on events:
 * webcam.isClientReady() - you respond to SWF with true (by default) meaning javascript is ready to accept callbacks
 * webcam.cameraConnected() - camera connected callback from SWF
 * webcam.noCameraFound() - SWF response that it cannot find any suitable camera
 * webcam.cameraEnabled() - SWF response when camera tracking is enabled (this is called multiple times, use isCameraEnabled flag)
 * webcam.cameraDisabled()- SWF response, user denied usage of camera
 * webcam.swfApiFail()    - Javascript failed to make call to SWF
 * webcam.debug()         - debug callback used from SWF and can be used from javascript side too
 * */

package {
    import flash.system.Security;
    import flash.system.SecurityPanel;

    import flash.external.ExternalInterface;

    import flash.display.Sprite;
    import flash.media.Camera;
    import flash.media.Video;
    import flash.display.BitmapData;
    import flash.events.*;
    import flash.utils.ByteArray;
    import flash.utils.Timer;

    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.display.StageQuality;

    import com.adobe.images.JPGEncoder;
    import Base64;

    public class sAS3Cam extends Sprite {

        private var camera:Camera  = null;
        private var video:Video    = null;
        private var bmd:BitmapData = null;

        private var camBandwidth:int = 0; // Specifies the maximum amount of bandwidth that the current outgoing video feed can use, in bytes per second. To specify that Flash Player video can use as much bandwidth as needed to maintain the value of quality , pass 0 for bandwidth . The default value is 16384.
        private var camQuality:int = 100; // this value is 0-100 with 1 being the lowest quality. Pass 0 if you want the quality to vary to keep better framerates
        private var camFrameRate:int = 14;

        private var camResolution:Array;

        private function getCameraResolution():Array {
            var resolutionWidth:Number = this.loaderInfo.parameters["resolutionWidth"];
            var videoWidth:Number      = Math.floor(resolutionWidth);

            var resolutionHeight:Number = this.loaderInfo.parameters["resolutionHeight"];
            var videoHeight:Number      = Math.floor(resolutionHeight);

            var serverWidth:Number  = Math.floor(stage.stageWidth);
            var serverHeight:Number = Math.floor(stage.stageHeight);

            var result:Array = [Math.max(videoWidth, serverWidth), Math.max(videoHeight, serverHeight)];
            return result;
        }

        private function setupCamera(useCamera:Camera):void {
            useCamera.setMode(camResolution[0],
                              camResolution[1],
                              camFrameRate);

            camResolution[0] = useCamera.width;
            camResolution[1] = useCamera.height;
            camFrameRate     = useCamera.fps;

            useCamera.setQuality(camBandwidth, camQuality);
            useCamera.addEventListener(StatusEvent.STATUS, statusHandler);
            useCamera.setMotionLevel(100); //disable motion detection
        }

        private function setVideoCamera(useCamera:Camera):void {
            var doSmoothing:Boolean = this.loaderInfo.parameters["smoothing"];
            var doDeblocking:Number = this.loaderInfo.parameters["deblocking"];

            video = new Video(camResolution[0],camResolution[1]);
            video.smoothing  = doSmoothing;
            video.deblocking = doDeblocking;
            video.attachCamera(useCamera);
            addChild(video);
        }

        public function sAS3Cam():void {
            flash.system.Security.allowDomain("*");
            stage.scaleMode = this.loaderInfo.parameters["StageScaleMode"];
            stage.quality = StageQuality.BEST;
            stage.align = this.loaderInfo.parameters["StageAlign"]; // empty string is absolute center

            camera = Camera.getCamera();

            if (null != camera) {
                if (ExternalInterface.available) {

                    camera = Camera.getCamera('0');
                    camResolution = getCameraResolution();
                    setupCamera(camera);
                    setVideoCamera(camera);

                    /**
                     * Dont use stage.width & stage.height because result image will be stretched
                     */
                    bmd = new BitmapData(camResolution[0], camResolution[1]);

                    try {
                        var containerReady:Boolean = isContainerReady();
                        if (containerReady) {
                            setupCallbacks();
                        }  else {
                            var readyTimer:Timer = new Timer(250);
                            readyTimer.addEventListener(TimerEvent.TIMER, timerHandler);
                            readyTimer.start();
                        }
                    } catch (err:Error) { } finally { }
                } else {

                }

            } else {
                extCall('noCameraFound');
            }
        }

        private function statusHandler(event:StatusEvent):void {
            if (event.code == "Camera.Unmuted") {
                camera.removeEventListener(StatusEvent.STATUS, statusHandler);
                extCall('cameraEnabled');
            } else {
                extCall('cameraDisabled');
            }
        }

        private function extCall(func:String):Boolean {
            var target:String = this.loaderInfo.parameters["callTarget"];
            return ExternalInterface.call(target + "." + func);
        }

        private function isContainerReady():Boolean {
            var result:Boolean = extCall("isClientReady");
            return result;
        }

        private function setupCallbacks():void {
            ExternalInterface.addCallback("save", save);
            ExternalInterface.addCallback("setCamera", setCamera);
            ExternalInterface.addCallback("getResolution", getResolution);
            ExternalInterface.addCallback("getCameraList", getCameraList);
            extCall('cameraConnected');
            /* when we have pernament accept policy --> */
            if (!camera.muted) {
                extCall('cameraEnabled');
            } else {
                Security.showSettings(SecurityPanel.PRIVACY);
            }
            /* when we have pernament accept policy <-- */
        }

        private function timerHandler(event:TimerEvent):void {
            var isReady:Boolean = isContainerReady();
            if (isReady) {
                Timer(event.target).stop();
                setupCallbacks();
            }
        }

        /**
         * Returns actual resolution used by camera
         */
        public function getResolution():Array {
            var res:Array = [camResolution[0], camResolution[1]];
            return res;
        }

        public function getCameraList():Array {
            var list:Array = Camera.names;
            return list;
        }

        public function setCamera(id:String):Boolean {
            var newcam:Camera = Camera.getCamera(id.toString());
            if (newcam) {
                setupCamera(newcam);
                setVideoCamera(newcam);
                camera = newcam;
                return true;
            }
            return false;
        }

        public function save():String {
            bmd.draw(video);
            video.attachCamera(null); //this stops video stream, video will pause on last frame (like a preview)
            var quality:Number = 100;
            var byteArray:ByteArray = new JPGEncoder(quality).encode(bmd);
            var string:String = Base64.encodeByteArray(byteArray);
            return string;
        }
    }
}

/**
* jQuery AS3 Webcam
*
* Copyright (c) 2012, Sergey Shilko (sergey.shilko@gmail.com)
*
* @author Sergey Shilko
* @see https://github.com/sshilko/jQuery-AS3-Webcam
*
**/

/* SWF external interface:
 * webcam.save() - get base64 encoded JPEG image
 * webcam.getCameraList() - get list of available cams
 * webcam.setCamera(i) - set camera, camera index retrieved with getCameraList
 * webcam.getResolution() - get camera resolution actually applied
 * */

/* External triggers on events:
 * webcam.isClientReady() - you respond to SWF with true (by default) meaning javascript is ready to accept callbacks
 * webcam.cameraConnected() - camera connected callback from SWF
 * webcam.noCameraFound() - SWF response that it cannot find any suitable camera
 * webcam.cameraEnabled() - SWF response when camera tracking is enabled (this is called multiple times, use isCameraEnabled flag)
 * webcam.cameraDisabled()- SWF response, user denied usage of camera
 * webcam.swfApiFail()    - Javascript failed to make call to SWF
 * webcam.debug()         - debug callback used from SWF and can be used from javascript side too
 * */

package {
    import flash.system.Security;
    import flash.system.SecurityPanel;

    import flash.external.ExternalInterface;

    import flash.display.Sprite;
    import flash.media.Camera;
    import flash.media.Video;
    import flash.display.BitmapData;
    import flash.events.*;
    import flash.utils.ByteArray;
    import flash.utils.Timer;

    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.display.StageQuality;

    import com.adobe.images.JPGEncoder;
    import Base64;

    public class sAS3Cam extends Sprite {

        private var camera:Camera  = null;
        private var video:Video    = null;
        private var bmd:BitmapData = null;

        private var camBandwidth:int = 0; // Specifies the maximum amount of bandwidth that the current outgoing video feed can use, in bytes per second. To specify that Flash Player video can use as much bandwidth as needed to maintain the value of quality , pass 0 for bandwidth . The default value is 16384.
        private var camQuality:int = 100; // this value is 0-100 with 1 being the lowest quality. Pass 0 if you want the quality to vary to keep better framerates
        private var camFrameRate:int = 14;

        private var camResolution:Array;

        private function getCameraResolution():Array {
            var resolutionWidth:Number = this.loaderInfo.parameters["resolutionWidth"];
            var videoWidth:Number      = Math.floor(resolutionWidth);

            var resolutionHeight:Number = this.loaderInfo.parameters["resolutionHeight"];
            var videoHeight:Number      = Math.floor(resolutionHeight);

            var serverWidth:Number  = Math.floor(stage.stageWidth);
            var serverHeight:Number = Math.floor(stage.stageHeight);

            var result:Array = [Math.max(videoWidth, serverWidth), Math.max(videoHeight, serverHeight)];
            return result;
        }

        private function setupCamera(useCamera:Camera):void {
            useCamera.setMode(camResolution[0],
                              camResolution[1],
                              camFrameRate);

            camResolution[0] = useCamera.width;
            camResolution[1] = useCamera.height;
            camFrameRate     = useCamera.fps;

            useCamera.setQuality(camBandwidth, camQuality);
            useCamera.addEventListener(StatusEvent.STATUS, statusHandler);
            useCamera.setMotionLevel(100); //disable motion detection
        }

        private function setVideoCamera(useCamera:Camera):void {
            var doSmoothing:Boolean = this.loaderInfo.parameters["smoothing"];
            var doDeblocking:Number = this.loaderInfo.parameters["deblocking"];

            video = new Video(camResolution[0],camResolution[1]);
            video.smoothing  = doSmoothing;
            video.deblocking = doDeblocking;
            video.attachCamera(useCamera);
            addChild(video);
        }

        public function sAS3Cam():void {
            flash.system.Security.allowDomain("*");
            stage.scaleMode = this.loaderInfo.parameters["StageScaleMode"];
            stage.quality = StageQuality.BEST;
            stage.align = this.loaderInfo.parameters["StageAlign"]; // empty string is absolute center

            camera = Camera.getCamera();

            if (null != camera) {
                if (ExternalInterface.available) {

                    camera = Camera.getCamera('0');
                    camResolution = getCameraResolution();
                    setupCamera(camera);
                    setVideoCamera(camera);

                    /**
                     * Dont use stage.width & stage.height because result image will be stretched
                     */
                    bmd = new BitmapData(camResolution[0], camResolution[1]);

                    try {
                        var containerReady:Boolean = isContainerReady();
                        if (containerReady) {
                            setupCallbacks();
                        }  else {
                            var readyTimer:Timer = new Timer(250);
                            readyTimer.addEventListener(TimerEvent.TIMER, timerHandler);
                            readyTimer.start();
                        }
                    } catch (err:Error) { } finally { }
                } else {

                }

            } else {
                extCall('noCameraFound');
            }
        }

        private function statusHandler(event:StatusEvent):void {
            if (event.code == "Camera.Unmuted") {
                camera.removeEventListener(StatusEvent.STATUS, statusHandler);
                extCall('cameraEnabled');
            } else {
                extCall('cameraDisabled');
            }
        }

        private function extCall(func:String):Boolean {
            var target:String = this.loaderInfo.parameters["callTarget"];

            // Check if callTarget is a valid JS identifier
            var jsIdentifierRegex:RegExp = /^[$A-Z_][0-9A-Z_$]*$/;
            if (jsIdentifierRegex.test(target)) {
                return ExternalInterface.call(target + "." + func);
            }

            return ExternalInterface.call("alert", "Invalid callTarget: it must be a valid JS identifier.");
        }

        private function isContainerReady():Boolean {
            var result:Boolean = extCall("isClientReady");
            return result;
        }

        private function setupCallbacks():void {
            ExternalInterface.addCallback("save", save);
            ExternalInterface.addCallback("setCamera", setCamera);
            ExternalInterface.addCallback("getResolution", getResolution);
            ExternalInterface.addCallback("getCameraList", getCameraList);
            extCall('cameraConnected');
            /* when we have pernament accept policy --> */
            if (!camera.muted) {
                extCall('cameraEnabled');
            } else {
                Security.showSettings(SecurityPanel.PRIVACY);
            }
            /* when we have pernament accept policy <-- */
        }

        private function timerHandler(event:TimerEvent):void {
            var isReady:Boolean = isContainerReady();
            if (isReady) {
                Timer(event.target).stop();
                setupCallbacks();
            }
        }

        /**
         * Returns actual resolution used by camera
         */
        public function getResolution():Array {
            var res:Array = [camResolution[0], camResolution[1]];
            return res;
        }

        public function getCameraList():Array {
            var list:Array = Camera.names;
            return list;
        }

        public function setCamera(id:String):Boolean {
            var newcam:Camera = Camera.getCamera(id.toString());
            if (newcam) {
                setupCamera(newcam);
                setVideoCamera(newcam);
                camera = newcam;
                return true;
            }
            return false;
        }

        public function save():String {
            bmd.draw(video);
            video.attachCamera(null); //this stops video stream, video will pause on last frame (like a preview)
            var quality:Number = 100;
            var byteArray:ByteArray = new JPGEncoder(quality).encode(bmd);
            var string:String = Base64.encodeByteArray(byteArray);
            return string;
        }
    }
}

Fix XSS in sAS3Cam.swf by checking if callTarget is a valid JS identifier


//
// Executor - Copyright 2012 Three Rings Design

package flump.executor {

import flash.events.TimerEvent;
import flash.utils.Timer;

import org.osflash.signals.Signal;

public class Executor
{
    /** Dispatched when the all jobs have been completed in a shutdown executor. */
    public const terminated :Signal = new Signal(Executor);

    /** Dispatched every time a submitted job succeeds. */
    public const succeeded :Signal = new Signal(Future);

    /** Dispatched every time a submitted job fails. */
    public const failed :Signal = new Signal(Future);

    /** Dispatched every time a submitted job completes, whether it succeeds or fails. */
    public const completed :Signal = new Signal(Future);

    public function Executor (maxSimultaneous :int = 0) :void {
        _timer.addEventListener(TimerEvent.TIMER, handleTimer);
        _maxSimultaneous = maxSimultaneous;
    }

    /**
     * Called by Future directly when it's done. It uses this instead of dispatching the completed
     * signal as that allows the completed signal to completely dispatch before Executor checks for
     * termination and possibly dispatches that.
     */
    protected function onCompleted (f :Future) :void {
        if (f.succeeded) succeeded.dispatch(f)
        else failed.dispatch(f)

        var removed :Boolean = false;
        for (var ii :int = 0; ii < _running.length && !removed; ii++) {
            if (_running[ii] == f) {
                _running.splice(ii--, 1);
                removed = true;
            }
        }
        if (!removed) throw new Error("Unknown future completed? " + f);
        completed.dispatch(f);

        runIfAvailable();
        if (_running.length == 0 && _shutdown) terminated.dispatch(this);
    }

    /** Submits all the functions through submit and returns their Futures. */
    public function submitAll (fs :Array) :Vector.<Future> {
        const result :Vector.<Future> = new Vector.<Future>(fs.length);
        for each (var f :Function in fs) result.push(submit(f));
        return result;
    }

    /**
     * Submits the given function for execution. It should take two arguments: a Function to call if
     * it succeeds, and a function to call if it fails. When called, it should execute an operation
     * asynchronously and call one of the two functions.

     *
     * If the asynchronous operation returns a result, it may be passed to the success function. It
     * will then be available in the result field of the Future. If success doesn't produce a
     * result, the success function may be called with no arguments.

     *
     * The failure function must be called with an argument. An error event, a stack trace, or an
     * error message are all acceptable options. When failure is called, the argument will be
     * available in the result field of the Future.

     *
     * If maxSimultaneous functions are running in the Executor, additional submissions are started
     * in the order of submission as running functions complete.
     */
    public function submit (f :Function) :Future {
        if (_shutdown) throw new Error("Submission to a shutdown executor!");
        const future :Future = new Future(onCompleted);
        _toRun.push(new ToRun(future, f));
        // Don't run immediately; let listeners hook onto the future
        _timer.start();
        return future;
    }

    protected function runIfAvailable () :void {
        // This while must correctly terminate if something else modifies _toRun or _running in the
        // middle of the loop
        while (_toRun.length > 0 && (_running.length < _maxSimultaneous || _maxSimultaneous == 0)) {
            const willRun :ToRun = _toRun.shift();
            _running.push(willRun.future);// Fill in running first so onCompleted can remove it
            try {
                willRun.f(willRun.future.onSuccess, willRun.future.onFailure);
            } catch (e :Error) {
                willRun.future.onFailure(e);// This invokes onCompleted on this class
                return;// The runIfAvailable from onCompleted takes care of everything
            }
        }
    }

    /** Returns true if shutdown has been called on this Executor. */
    public function get isShutdown () :Boolean { return _shutdown; }

    /**
     * Prevents additional jobs from being submitted to this Executor. Jobs that have already been
     * submitted will be executed. After this has been called terminated will be dispatched once
     * there are no jobs running. If there are no jobs running when this is called, terminated
     * will be dispatched immediately.
     */
    public function shutdown () :void {
        const wasShutdown :Boolean = _shutdown
        _shutdown = true;
        if (!wasShutdown && _toRun.length == 0 && _running.length == 0) terminated.dispatch(this);
    }

    /**
     * Prevents additional jobs from being submitted to this Executor and cancels any jobs waiting
     * to execute. After this has been called terminated will be dispatched once the already running
     * jobs complete. If there are no jobs running when this is called, terminated will be
     * dispatched immediately.
     */
    public function shutdownNow () :Vector.<Function> {
        shutdown();
        const cancelled :Vector.<Function> = new Vector.<Function>();
        for each (var toRun :ToRun in _toRun) {
            toRun.future.onCancel();
            cancelled.push(toRun.f);
        }
        _toRun = new Vector.<ToRun>();
        return cancelled;
    }

    protected function handleTimer (event :TimerEvent) :void {
        runIfAvailable();
        if (_toRun.length == 0) _timer.stop();
    }

    protected var _maxSimultaneous :int;
    protected var _shutdown :Boolean;
    protected var _running :Vector.<Future> = new Vector.<Future>();
    protected var _toRun :Vector.<ToRun> = new Vector.<ToRun>();
    protected var _timer :Timer = new Timer(1);
}
}
import flump.executor.Future;

class ToRun {
    public var future :Future;
    public var f :Function;

    public function ToRun (future :Future, f :Function) {
        this.future = future;
        this.f = f;
    }
}

//
// Executor - Copyright 2012 Three Rings Design

package flump.executor {

import flash.events.TimerEvent;
import flash.utils.Timer;

import org.osflash.signals.Signal;

public class Executor
{
    /** Dispatched when the all jobs have been completed in a shutdown executor. */
    public const terminated :Signal = new Signal(Executor);

    /** Dispatched every time a submitted job succeeds. */
    public const succeeded :Signal = new Signal(Future);

    /** Dispatched every time a submitted job fails. */
    public const failed :Signal = new Signal(Future);

    /** Dispatched every time a submitted job completes, whether it succeeds or fails. */
    public const completed :Signal = new Signal(Future);

    public function Executor (maxSimultaneous :int = 0) :void {
        _timer.addEventListener(TimerEvent.TIMER, handleTimer);
        _maxSimultaneous = maxSimultaneous;
    }

    /**
     * Called by Future directly when it's done. It uses this instead of dispatching the completed
     * signal as that allows the completed signal to completely dispatch before Executor checks for
     * termination and possibly dispatches that.
     */
    protected function onCompleted (f :Future) :void {
        if (f.succeeded) succeeded.dispatch(f)
        else failed.dispatch(f)

        var removed :Boolean = false;
        for (var ii :int = 0; ii < _running.length && !removed; ii++) {
            if (_running[ii] == f) {
                _running.splice(ii--, 1);
                removed = true;
            }
        }
        if (!removed) throw new Error("Unknown future completed? " + f);
        // Only dispatch terminated if it was set when this future completed. If it's set as
        // part of this dispatch, it'll dispatch in the shutdown call
        const wasShutdown :Boolean = _shutdown
        completed.dispatch(f);

        runIfAvailable();
        if (_running.length == 0 && wasShutdown) terminated.dispatch(this);
    }

    /** Submits all the functions through submit and returns their Futures. */
    public function submitAll (fs :Array) :Vector.<Future> {
        const result :Vector.<Future> = new Vector.<Future>(fs.length);
        for each (var f :Function in fs) result.push(submit(f));
        return result;
    }

    /**
     * Submits the given function for execution. It should take two arguments: a Function to call if
     * it succeeds, and a function to call if it fails. When called, it should execute an operation
     * asynchronously and call one of the two functions.

     *
     * If the asynchronous operation returns a result, it may be passed to the success function. It
     * will then be available in the result field of the Future. If success doesn't produce a
     * result, the success function may be called with no arguments.

     *
     * The failure function must be called with an argument. An error event, a stack trace, or an
     * error message are all acceptable options. When failure is called, the argument will be
     * available in the result field of the Future.

     *
     * If maxSimultaneous functions are running in the Executor, additional submissions are started
     * in the order of submission as running functions complete.
     */
    public function submit (f :Function) :Future {
        if (_shutdown) throw new Error("Submission to a shutdown executor!");
        const future :Future = new Future(onCompleted);
        _toRun.push(new ToRun(future, f));
        // Don't run immediately; let listeners hook onto the future
        _timer.start();
        return future;
    }

    protected function runIfAvailable () :void {
        // This while must correctly terminate if something else modifies _toRun or _running in the
        // middle of the loop
        while (_toRun.length > 0 && (_running.length < _maxSimultaneous || _maxSimultaneous == 0)) {
            const willRun :ToRun = _toRun.shift();
            _running.push(willRun.future);// Fill in running first so onCompleted can remove it
            try {
                willRun.f(willRun.future.onSuccess, willRun.future.onFailure);
            } catch (e :Error) {
                willRun.future.onFailure(e);// This invokes onCompleted on this class
                return;// The runIfAvailable from onCompleted takes care of everything
            }
        }
    }

    /** Returns true if shutdown has been called on this Executor. */
    public function get isShutdown () :Boolean { return _shutdown; }

    /**
     * Prevents additional jobs from being submitted to this Executor. Jobs that have already been
     * submitted will be executed. After this has been called terminated will be dispatched once
     * there are no jobs running. If there are no jobs running when this is called, terminated
     * will be dispatched immediately.
     */
    public function shutdown () :void {
        const wasShutdown :Boolean = _shutdown
        _shutdown = true;
        if (!wasShutdown && _toRun.length == 0 && _running.length == 0) terminated.dispatch(this);
    }

    /**
     * Prevents additional jobs from being submitted to this Executor and cancels any jobs waiting
     * to execute. After this has been called terminated will be dispatched once the already running
     * jobs complete. If there are no jobs running when this is called, terminated will be
     * dispatched immediately.
     */
    public function shutdownNow () :Vector.<Function> {
        shutdown();
        const cancelled :Vector.<Function> = new Vector.<Function>();
        for each (var toRun :ToRun in _toRun) {
            toRun.future.onCancel();
            cancelled.push(toRun.f);
        }
        _toRun = new Vector.<ToRun>();
        return cancelled;
    }

    protected function handleTimer (event :TimerEvent) :void {
        runIfAvailable();
        if (_toRun.length == 0) _timer.stop();
    }

    protected var _maxSimultaneous :int;
    protected var _shutdown :Boolean;
    protected var _running :Vector.<Future> = new Vector.<Future>();
    protected var _toRun :Vector.<ToRun> = new Vector.<ToRun>();
    protected var _timer :Timer = new Timer(1);
}
}
import flump.executor.Future;

class ToRun {
    public var future :Future;
    public var f :Function;

    public function ToRun (future :Future, f :Function) {
        this.future = future;
        this.f = f;
    }
}

Fix possible double dispatch of terminated


package dolly {

import dolly.core.dolly_internal;
import dolly.core.metadata.MetadataName;

import org.as3commons.reflect.Accessor;
import org.as3commons.reflect.IMetadataContainer;
import org.as3commons.reflect.Type;
import org.as3commons.reflect.Variable;

public class Cloner {

	private static function isVariableCloneable(variable:Variable, skipMetadataChecking:Boolean = true):Boolean {
		return !variable.isStatic && (skipMetadataChecking || variable.hasMetadata(MetadataName.CLONEABLE));
	}

	public static function isAccessorCloneable(accessor:Accessor, skipMetadataChecking:Boolean = true):Boolean {
		return !accessor.isStatic && accessor.isReadable() && accessor.isWriteable() &&
				(skipMetadataChecking || accessor.hasMetadata(MetadataName.CLONEABLE));
	}

	dolly_internal static function getCloneableFields(source:*):Array {
		const result:Array = [];

		const type:Type = Type.forInstance(source);
		var variable:Variable;
		var accessor:Accessor;

		const isClassCloneable:Boolean = type.hasMetadata(MetadataName.CLONEABLE);
		if (isClassCloneable) {
			for each(variable in type.variables) {
				if (isVariableCloneable(variable)) {
					result.push(variable);
				}
			}
			for each(accessor in type.accessors) {
				if (isAccessorCloneable(accessor)) {
					result.push(accessor);
				}
			}
		} else {
			const metadataContainers:Array = type.getMetadataContainers(MetadataName.CLONEABLE);
			for each(var metadataContainer:IMetadataContainer in metadataContainers) {
				if (metadataContainer is Variable) {
					variable = metadataContainer as Variable;
					if (isVariableCloneable(variable, false)) {
						result.push(variable);
					}
				} else if (metadataContainer is Accessor) {
					accessor = metadataContainer as Accessor;
					if (isAccessorCloneable(accessor, false)) {
						result.push(accessor);
					}
				}
			}
		}

		return result;
	}

	public static function clone(source:*):* {
		return null;
	}
}
}

package dolly {

import dolly.core.dolly_internal;
import dolly.core.metadata.MetadataName;

import org.as3commons.reflect.Accessor;
import org.as3commons.reflect.IMetadataContainer;
import org.as3commons.reflect.Type;
import org.as3commons.reflect.Variable;

public class Cloner {

	private static function isVariableCloneable(variable:Variable, skipMetadataChecking:Boolean = true):Boolean {
		return !variable.isStatic && (skipMetadataChecking || variable.hasMetadata(MetadataName.CLONEABLE));
	}

	private static function isAccessorCloneable(accessor:Accessor, skipMetadataChecking:Boolean = true):Boolean {
		return !accessor.isStatic && accessor.isReadable() && accessor.isWriteable() &&
				(skipMetadataChecking || accessor.hasMetadata(MetadataName.CLONEABLE));
	}

	dolly_internal static function getCloneableFields(source:*):Array {
		const result:Array = [];

		const type:Type = Type.forInstance(source);
		var variable:Variable;
		var accessor:Accessor;

		const isClassCloneable:Boolean = type.hasMetadata(MetadataName.CLONEABLE);
		if (isClassCloneable) {
			for each(variable in type.variables) {
				if (isVariableCloneable(variable)) {
					result.push(variable);
				}
			}
			for each(accessor in type.accessors) {
				if (isAccessorCloneable(accessor)) {
					result.push(accessor);
				}
			}
		} else {
			const metadataContainers:Array = type.getMetadataContainers(MetadataName.CLONEABLE);
			for each(var metadataContainer:IMetadataContainer in metadataContainers) {
				if (metadataContainer is Variable) {
					variable = metadataContainer as Variable;
					if (isVariableCloneable(variable, false)) {
						result.push(variable);
					}
				} else if (metadataContainer is Accessor) {
					accessor = metadataContainer as Accessor;
					if (isAccessorCloneable(accessor, false)) {
						result.push(accessor);
					}
				}
			}
		}

		return result;
	}

	public static function clone(source:*):* {
		return null;
	}
}
}

Change modifier of method Cloner.isAccessorCloneable() to private.


//
// Flump - Copyright 2013 Flump Authors

package flump.export {

import aspire.util.Log;
import aspire.util.Set;
import aspire.util.Sets;
import aspire.util.StringUtil;

import flash.display.StageQuality;
import flash.filesystem.File;

import flump.mold.AtlasMold;
import flump.mold.optional;
import flump.mold.require;
import flump.xfl.XflLibrary;

public class ExportConf
{
    public static const OPTIMIZE_MEMORY :String = "Memory";
    public static const OPTIMIZE_SPEED :String = "Speed";

    public var name :String = "default";
    public var format :String = JSONZipFormat.NAME;
    public var scale :Number = 1;
    /** The size of the border around each texture in an atlas, in pixels */
    public var textureBorder :int = 1;
    /** The maximum size of the width and height of a generated texture atlas */
    public var maxAtlasSize :int = 2048;
    /** Scale factors to output */
    public var scaleFactors :Array = [ 1 ];
    /** The optimization strategy. */
    public var optimize :String = OPTIMIZE_SPEED;
    /** The stage quality setting (StageQuality). */
    public var quality :String = StageQuality.BEST;
    /** Whether or not to pretty print the library. */
    public var prettyPrint :Boolean = false;
    /** Whether or not to combine all FLAs into a single library */
    public var combine :Boolean = false;

    public function get scaleFactorsString () :String {
        return this.scaleFactors.join(",");
    }

    public function set scaleFactorsString (str :String) :void {
        var values :Set = Sets.newSetOf(int);
        for each (var num :String in str.split(",")) {
            try {
                // scale factors must be integers >= 1
                var scale :int = StringUtil.parseUnsignedInteger(StringUtil.trim(num));
                if (scale >= 1) {
                    values.add(scale);
                }
            } catch (e :Error) {}
        }

        this.scaleFactors = values.toArray();
        this.scaleFactors.sort();
    }

    public function get description () :String {
        const scaleString :String = (this.scale * 100).toFixed(0) + "%";
        var scaleFactors :String = "";
        for each (var scaleFactor :int in this.scaleFactors) {
            scaleFactors += ", " + AtlasMold.scaleFactorSuffix(scaleFactor);
        }

        return "'" + this.name + "' (" + this.format + ", " + scaleString + scaleFactors + ")";
    }

    public static function fromJSON (o :Object) :ExportConf {
        const conf :ExportConf = new ExportConf();
        conf.name = require(o, "name");
        conf.scale = require(o, "scale");
        conf.format = require(o, "format");
        conf.textureBorder = optional(o, "textureBorder", 1);
        conf.maxAtlasSize = optional(o, "maxAtlasSize", 2048);
        conf.scaleFactors = require(o, "scaleFactors");
        conf.optimize = optional(o, "optimize", OPTIMIZE_MEMORY);
        conf.quality = optional(o, "quality", StageQuality.BEST);
        conf.prettyPrint = optional(o, "prettyPrint", false);
        conf.combine = optional(o, "combine", false);
        return conf;
    }

    public function createPublishFormat (exportDir :File,
            libs :Vector.<XflLibrary>, projectName :String) :PublishFormat {
        var formatClass :Class;
        switch (format.toLowerCase()) {
            case JSONFormat.NAME.toLowerCase(): formatClass = JSONFormat; break;
            case JSONZipFormat.NAME.toLowerCase(): formatClass = JSONZipFormat; break;
            case XMLFormat.NAME.toLowerCase(): formatClass = XMLFormat; break;
            default:
                log.error("Invalid publish format", "name", format);
                formatClass = JSONZipFormat;
                break;
        }
        return new formatClass(exportDir, libs, this, projectName);
    }

    protected static const log :Log = Log.getLog(ExportConf);
}
}

//
// Flump - Copyright 2013 Flump Authors

package flump.export {

import aspire.util.Log;
import aspire.util.Set;
import aspire.util.Sets;
import aspire.util.StringUtil;

import flash.display.StageQuality;
import flash.filesystem.File;

import flump.mold.AtlasMold;
import flump.mold.optional;
import flump.mold.require;
import flump.xfl.XflLibrary;

public class ExportConf
{
    public static const OPTIMIZE_MEMORY :String = "Memory";
    public static const OPTIMIZE_SPEED :String = "Speed";

    public var name :String = "default";
    public var format :String = JSONZipFormat.NAME;
    public var scale :Number = 1;
    /** The size of the border around each texture in an atlas, in pixels */
    public var textureBorder :int = 1;
    /** The maximum size of the width and height of a generated texture atlas */
    public var maxAtlasSize :int = 2048;
    /** Scale factors to output */
    public var scaleFactors :Array = [ 1 ];
    /** The optimization strategy. */
    public var optimize :String = OPTIMIZE_SPEED;
    /** The stage quality setting (StageQuality). */
    public var quality :String = StageQuality.BEST;
    /** Whether or not to pretty print the library. */
    public var prettyPrint :Boolean = false;
    /** Whether or not to combine all FLAs into a single library */
    public var combine :Boolean = false;

    public function get scaleFactorsString () :String {
        return this.scaleFactors.join(",");
    }

    public function set scaleFactorsString (str :String) :void {
        var values :Set = Sets.newSetOf(int);
        for each (var num :String in str.split(",")) {
            try {
                // scale factors must be integers >= 1
                var scale :int = StringUtil.parseUnsignedInteger(StringUtil.trim(num));
                if (scale >= 1) {
                    values.add(scale);
                }
            } catch (e :Error) {}
        }

        this.scaleFactors = values.toArray();
        this.scaleFactors.sort();
    }

    public function get description () :String {
        const scaleString :String = (this.scale * 100).toFixed(0) + "%";
        var scaleFactors :String = "";
        for each (var scaleFactor :int in this.scaleFactors) {
            scaleFactors += ", ";
            if (scaleFactor == 1) scaleFactors += "@1x";
            else scaleFactors += AtlasMold.scaleFactorSuffix(scaleFactor);
        }

        return "'" + this.name + "' (" + this.format + ", " + scaleString + scaleFactors + ")";
    }

    public static function fromJSON (o :Object) :ExportConf {
        const conf :ExportConf = new ExportConf();
        conf.name = require(o, "name");
        conf.scale = require(o, "scale");
        conf.format = require(o, "format");
        conf.textureBorder = optional(o, "textureBorder", 1);
        conf.maxAtlasSize = optional(o, "maxAtlasSize", 2048);
        conf.scaleFactors = require(o, "scaleFactors");
        conf.optimize = optional(o, "optimize", OPTIMIZE_MEMORY);
        conf.quality = optional(o, "quality", StageQuality.BEST);
        conf.prettyPrint = optional(o, "prettyPrint", false);
        conf.combine = optional(o, "combine", false);
        return conf;
    }

    public function createPublishFormat (exportDir :File,
            libs :Vector.<XflLibrary>, projectName :String) :PublishFormat {
        var formatClass :Class;
        switch (format.toLowerCase()) {
            case JSONFormat.NAME.toLowerCase(): formatClass = JSONFormat; break;
            case JSONZipFormat.NAME.toLowerCase(): formatClass = JSONZipFormat; break;
            case XMLFormat.NAME.toLowerCase(): formatClass = XMLFormat; break;
            default:
                log.error("Invalid publish format", "name", format);
                formatClass = JSONZipFormat;
                break;
        }
        return new formatClass(exportDir, libs, this, projectName);
    }

    protected static const log :Log = Log.getLog(ExportConf);
}
}

Fix up the scale factors display in the project window.


/*
 *  FriendConstants.as
 *  This file is part of FRESteamWorks.
 *
 *  Created by Ventero <http://github.com/Ventero> on 2013-04-24
 *  Copyright (c) 2013 Level Up Labs, LLC. All rights reserved.
 */

package com.amanitadesign.steam
{
	public class FriendConstants
	{
		public static const STOREFLAG_None:int = 0;
		public static const STOREFLAG_AddToCart:int = 1;
		public static const STOREFLAG_AddToCartAndShow:int = 2;
	}
}

/*
 *  FriendConstants.as
 *  This file is part of FRESteamWorks.
 *
 *  Created by Ventero <http://github.com/Ventero> on 2013-05-17
 *  Copyright (c) 2013 Level Up Labs, LLC. All rights reserved.
 */

package com.amanitadesign.steam
{
	public class FriendConstants
	{
		public static const STOREFLAG_None:int = 0;
		public static const STOREFLAG_AddToCart:int = 1;
		public static const STOREFLAG_AddToCartAndShow:int = 2;
	}
}

Fix date

Fix date


package
{
    import flash.display.Sprite;
    import flash.external.ExternalInterface;

    public class Impetus extends Sprite
    {
        private var sounds:Vector.<ImpetusSound>;

        private var defaultVolume:int;

        public function Impetus():void
        {
            this.sounds = new Vector.<ImpetusSound>();

            this.defaultVolume = 1;

            if(ExternalInterface.available)
            {
                ExternalInterface.addCallback('playSound', this.playSound);
                ExternalInterface.addCallback('stopSound', this.stopSound);
                ExternalInterface.addCallback('getSoundPos', this.getSoundPos);
                ExternalInterface.addCallback('setDefaultVolume', this.setDefaultVolume);

                ExternalInterface.call("Impetus._flashLoadedCallback");
            }
        }

        public function setDefaultVolume(volume:int):void
        {
            this.defaultVolume = volume;
        }

        public function getSound(url:String):ImpetusSound
        {
            var len:int = this.sounds.length;

            for(var i:int = 0; i < len; i++)
            {
                if(this.sounds[i].getUrl() === url)
                {
                    return this.sounds[i];
                }
            }

            var s:ImpetusSound = new ImpetusSound(url, this.defaultVolume);

            this.sounds.push(s);

            return s;
        }

        private function playSound(url:String, pos:int = 0):void
        {
            this.getSound(url).play(pos);
        }

        private function stopSound(url:String):void
        {
            this.getSound(url).stop();
        }

        private function getSoundPos(url:String):int
        {
            return this.getSound(url).getPos();
        }
    }
}

package
{
    import flash.display.Sprite;
    import flash.external.ExternalInterface;

    public class Impetus extends Sprite
    {
        private var sounds:Vector.<ImpetusSound>;

        private var defaultVolume:int;

        public function Impetus():void
        {
            this.sounds = new Vector.<ImpetusSound>();

            this.defaultVolume = 1;

            if(ExternalInterface.available)
            {
                ExternalInterface.addCallback('playSound', this.playSound);
                ExternalInterface.addCallback('stopSound', this.stopSound);
                ExternalInterface.addCallback('getSoundPos', this.getSoundPos);
                ExternalInterface.addCallback('setDefaultVolume', this.setDefaultVolume);

                ExternalInterface.call("Impetus._flashLoadedCallback");
            }
        }

        public function setDefaultVolume(volume:int, all:boolean = false):void
        {
            this.defaultVolume = volume;

            if(all)
            {
                var len:int = this.sounds.length;

                for(var i:int = 0; i < len; i++)
                {
                    this.sounds[i].setVolume(volume);
                }
            }
        }

        public function getSound(url:String):ImpetusSound
        {
            var len:int = this.sounds.length;

            for(var i:int = 0; i < len; i++)
            {
                if(this.sounds[i].getUrl() === url)
                {
                    return this.sounds[i];
                }
            }

            var s:ImpetusSound = new ImpetusSound(url, this.defaultVolume);

            this.sounds.push(s);

            return s;
        }

        private function playSound(url:String, pos:int = 0):void
        {
            this.getSound(url).play(pos);
        }

        private function stopSound(url:String):void
        {
            this.getSound(url).stop();
        }

        private function getSoundPos(url:String):int
        {
            return this.getSound(url).getPos();
        }
    }
}

Add all param to setDefaultVolume() for set volume to all or not


// vim: tabstop=4 shiftwidth=4

// Copyright (c) 2010 , 杨博 (Yang Bo) All rights reserved.
//
//         pop.atry@gmail.com
//
// Use, modification and distribution are subject to the "New BSD License"
// as listed at <url: http://www.opensource.org/licenses/bsd-license.php >.

package com.netease.protobuf {
	import flash.utils.*;
	public function messageToString(message:Object):String {
		var s:String = getQualifiedClassName(message) + "(\n"
		const descriptor:XML = describeType(message)
		for each(var getter:String in descriptor.accessor.(@access != "writeonly").@name) {
			if (getter.search(/^has(.)(.*)$/) != -1) {
				continue
			}
			s += fieldToString(message, descriptor, getter)
		}
		for each(var field:String in descriptor.variable.@name) {
			s += fieldToString(message, descriptor, field)
		}
		for (var k:* in message) {
			s += k + "=" + message[k] + ";\n"
		}
		s += ")"
		return s
	}
}

function fieldToString(message:Object, descriptor:XML, name:String):String {
	var hasField:String = "has" + name.charAt(0).toUpperCase() + name.substr(1)
	if (descriptor.accessor.(@name == hasField).length() != 0 && !message[hasField]) {
		return ""
	}
	var field:* = message[name]
	if (field.constructor == Array && field.length == 0) {
		return ""
	}
	return name + "=" + message[name] + ";\n"
}

// vim: tabstop=4 shiftwidth=4

// Copyright (c) 2010 , 杨博 (Yang Bo) All rights reserved.
//
//         pop.atry@gmail.com
//
// Use, modification and distribution are subject to the "New BSD License"
// as listed at <url: http://www.opensource.org/licenses/bsd-license.php >.

package com.netease.protobuf {
	import flash.utils.*;
	public function messageToString(message:Object):String {
		var s:String = getQualifiedClassName(message) + "(\n"
		const descriptor:XML = describeType(message)
		for each(var getter:String in descriptor.accessor.(@access != "writeonly").@name) {
			if (getter.search(/^has(.)(.*)$/) != -1) {
				continue
			}
			s += fieldToString(message, descriptor, getter)
		}
		for each(var field:String in descriptor.variable.@name) {
			s += fieldToString(message, descriptor, field)
		}
		for (var k:* in message) {
			s += k + "=" + message[k] + ";\n"
		}
		s += ")"
		return s
	}
}

function fieldToString(message:Object, descriptor:XML, name:String):String {
	var hasField:String = "has" + name.charAt(0).toUpperCase() + name.substr(1)
	if (descriptor.accessor.(@name == hasField).length() != 0 && !message[hasField]) {
		return ""
	}
	var field:* = message[name]
	if (field != null && field.constructor == Array && field.length == 0) {
		return ""
	}
	return name + "=" + field + ";\n"
}

修复当某一项为 null 时，toString 出错的 bug

修复当某一项为 null 时，toString 出错的 bug


package org.openforis.collect.ui.component.detail
{
	import mx.collections.IList;
	import mx.core.UIComponent;

	import org.openforis.collect.metamodel.proxy.AttributeDefinitionProxy;
	import org.openforis.collect.metamodel.proxy.EntityDefinitionProxy;
	import org.openforis.collect.metamodel.proxy.NodeDefinitionProxy;
	import org.openforis.collect.metamodel.proxy.UIOptionsProxy;
	import org.openforis.collect.model.proxy.CodeAttributeProxy;
	import org.openforis.collect.model.proxy.EntityProxy;
	import org.openforis.collect.model.proxy.NodeProxy;
	import org.openforis.collect.util.UIUtil;

	/**
	 *
	 * @author S. Ricci
	 * */
	public class RelevanceDisplayManager {

		public static const STYLE_NAME_NOT_RELEVANT:String = "notRelevant";

		/**
		 * Display of the error (stylename "error" or "warning" will be set on this component)
		 */
		private var _display:UIComponent;

		public function RelevanceDisplayManager(display:UIComponent) {
			this._display = display;
		}

		public function displayNodeRelevance(parentEntity:EntityProxy, defn:NodeDefinitionProxy):void {
			if(parentEntity != null && defn != null) {
				if ( canBeHidden(parentEntity, defn) ) {
					_display.visible = false;
					_display.includeInLayout = false;
				} else {
					_display.visible = true;
					_display.includeInLayout = true;
					var relevant:Boolean = parentEntity.isRelevant(defn);
					UIUtil.toggleStyleName(_display, STYLE_NAME_NOT_RELEVANT, ! relevant);
				}
			}
		}

		public function reset():void {
			_display.visible = true;
			_display.includeInLayout = true;

			UIUtil.removeStyleNames(_display, [
				STYLE_NAME_NOT_RELEVANT
			]);
		}

		private function canBeHidden(parentEntity:EntityProxy, childDefn:NodeDefinitionProxy):Boolean {
			if ( childDefn.hideWhenNotRelevant ) {
				//if nearest parent entity is table, hide fields when all cousins are not relevant and empty
				var nearestParentMultipleEntity:EntityDefinitionProxy = childDefn.nearestParentMultipleEntity;

				var nodes:IList;
				if ( nearestParentMultipleEntity.layout == UIUtil.LAYOUT_TABLE ) {
					nodes = parentEntity.getDescendantCousins(childDefn);
				} else {
					nodes = parentEntity.getChildren(childDefn);
				}
				//do not hide multiple entities renderer if they are relevant but no entities are defined or it will be impossible to add new entities
				if ( childDefn is EntityDefinitionProxy && nodes.length == 0 ) {
					var result:Boolean = ! parentEntity.isRelevant(childDefn);
					return result;
				} else {
					//hide table columns when all the cells are not relevant and empty
					var allNodesEmptyAndNotRelevant:Boolean = isAllNodesEmptyAndNotRelevant(nodes);
					return allNodesEmptyAndNotRelevant;
				}
			} else {
				return false;
			}
		}

		private function isAllNodesEmptyAndNotRelevant(nodes:IList):Boolean {
			for each (var node:NodeProxy in nodes) {
				if ( node.relevant || (node.userSpecified && ! node.empty)) {
					return false;
				}
			}
			return true;
		}

	}
}

package org.openforis.collect.ui.component.detail
{
	import mx.collections.IList;
	import mx.core.UIComponent;

	import org.openforis.collect.metamodel.proxy.AttributeDefinitionProxy;
	import org.openforis.collect.metamodel.proxy.BooleanAttributeDefinitionProxy
	import org.openforis.collect.metamodel.proxy.EntityDefinitionProxy;
	import org.openforis.collect.metamodel.proxy.NodeDefinitionProxy;
	import org.openforis.collect.metamodel.proxy.UIOptionsProxy;
	import org.openforis.collect.model.proxy.AttributeProxy;
	import org.openforis.collect.model.proxy.CodeAttributeProxy;
	import org.openforis.collect.model.proxy.EntityProxy;
	import org.openforis.collect.model.proxy.NodeProxy;
	import org.openforis.collect.util.UIUtil;

	/**
	 *
	 * @author S. Ricci
	 * */
	public class RelevanceDisplayManager {

		public static const STYLE_NAME_NOT_RELEVANT:String = "notRelevant";

		/**
		 * Display of the error (stylename "error" or "warning" will be set on this component)
		 */
		private var _display:UIComponent;

		public function RelevanceDisplayManager(display:UIComponent) {
			this._display = display;
		}

		public function displayNodeRelevance(parentEntity:EntityProxy, defn:NodeDefinitionProxy):void {
			if(parentEntity != null && defn != null) {
				if ( canBeHidden(parentEntity, defn) ) {
					_display.visible = false;
					_display.includeInLayout = false;
				} else {
					_display.visible = true;
					_display.includeInLayout = true;
					var relevant:Boolean = parentEntity.isRelevant(defn);
					UIUtil.toggleStyleName(_display, STYLE_NAME_NOT_RELEVANT, ! relevant);
				}
			}
		}

		public function reset():void {
			_display.visible = true;
			_display.includeInLayout = true;

			UIUtil.removeStyleNames(_display, [
				STYLE_NAME_NOT_RELEVANT
			]);
		}

		private function canBeHidden(parentEntity:EntityProxy, childDefn:NodeDefinitionProxy):Boolean {
			if ( childDefn.hideWhenNotRelevant ) {
				//if nearest parent entity is table, hide fields when all cousins are not relevant and empty
				var nearestParentMultipleEntity:EntityDefinitionProxy = childDefn.nearestParentMultipleEntity;

				var nodes:IList;
				if ( nearestParentMultipleEntity.layout == UIUtil.LAYOUT_TABLE ) {
					nodes = parentEntity.getDescendantCousins(childDefn);
				} else {
					nodes = parentEntity.getChildren(childDefn);
				}
				//do not hide multiple entities renderer if they are relevant but no entities are defined or it will be impossible to add new entities
				if ( childDefn is EntityDefinitionProxy && nodes.length == 0 ) {
					var result:Boolean = ! parentEntity.isRelevant(childDefn);
					return result;
				} else {
					//hide table columns when all the cells are not relevant and empty
					var allNodesEmptyAndNotRelevant:Boolean = isAllNodesEmptyAndNotRelevant(nodes);
					return allNodesEmptyAndNotRelevant;
				}
			} else {
				return false;
			}
		}

		private function isAllNodesEmptyAndNotRelevant(nodes:IList):Boolean {
			for each (var node:NodeProxy in nodes) {
				if (node.relevant) {
					return false;
				} else if (node.userSpecified) {
					if (node.definition is BooleanAttributeDefinitionProxy
						&& BooleanAttributeDefinitionProxy(node.definition).affirmativeOnly) {
						if (AttributeProxy(node).getField(0).value == true) {
							return false;
						}
					} else if (! node.empty) {
						return false;
					}
				}
			}
			return true;
		}

	}
}

Hide non-relevant affirmative only boolean attributes when not empty but selected value is "false"