package krewfw.core_internal {

    import flash.media.Sound;

    import starling.display.Image;
    import starling.textures.Texture;
    import starling.utils.AssetManager;

    import krewfw.KrewConfig;
    import krewfw.utils.krew;

    //------------------------------------------------------------
    public class KrewResourceManager {

        private var _urlScheme:String;
        private var _globalScopeAssets:AssetManager;
        private var _sceneScopeAssets :AssetManager;

        //------------------------------------------------------------
        public function KrewResourceManager() {
            _urlScheme = KrewConfig.ASSET_URL_SCHEME;

            _globalScopeAssets = new KrewConfig.ASSET_MANAGER_CLASS;
            _sceneScopeAssets  = new KrewConfig.ASSET_MANAGER_CLASS;

            _globalScopeAssets.verbose = KrewConfig.ASSET_MANAGER_VERBOSE;
            _sceneScopeAssets .verbose = KrewConfig.ASSET_MANAGER_VERBOSE;
        }

        //------------------------------------------------------------
        // accessor
        //------------------------------------------------------------

        public function get globalAssetManager():AssetManager {
            return _globalScopeAssets;
        }

        public function get sceneAssetManager():AssetManager {
            return _sceneScopeAssets;
        }

        //------------------------------------------------------------

        /**
         * シーン単位で使いたいリソースのロード.
         * 基本的に starling.utils.AssetManager をラップしているだけなので
         * そちらのドキュメントを見よ。
         *
         * <ul>
         *   <li> テクスチャアトラスを読み込む場合は単純に png と xml の 2 つを指定すればよい。
         *        getImage("拡張子を除くファイル名") で取得できる。
         *        画像はファイル名で引くため、全アトラスでソース画像のファイル名（というか xml 内の識別子）
         *        がユニークになっている必要がある
         *   </li>
         *   <li> mp3  はロード後 getSound()  で取得できる </li>
         *   <li> json も読める。 getObject() で取得できる </li>
         *   <li> xml  も読める。 getXml()    で取得できる </li>
         *
         *   <li> ビットマップフォントは png と fnt を読み込み後、自動的に
         *        starling.text.TextField で使用可能になる
         *   </li>
         * </ul>
         */
        public function loadResources(fileNameList:Array, onLoadProgress:Function,
                                      onLoadComplete:Function):void
        {
            var suitablePathList:Array = _mapToSuitablePath(fileNameList);
            _sceneScopeAssets.enqueue(suitablePathList);

            _sceneScopeAssets.loadQueue(function(loadRatio:Number):void {
                if (onLoadProgress != null) {
                    onLoadProgress(loadRatio);
                }
                if (loadRatio == 1) {
                    onLoadComplete();
                }
            });
        }

        /**
         * シーン遷移時にこれを呼んでメモリ解放（テクスチャの dispose を呼ぶ）
         */
        public function purgeSceneScopeResources():void {
            _sceneScopeAssets.purge();
        }

        /**
         * ゲームを通してずっとメモリに保持しておくリソースのロード.
         * （Loading 画面のアセットなど）
         * 使い方は loadResources と同じ。
         * ロードしたものの取得は getGlobalImage() などと Global がついたメソッドで。
         *
         * 現状はゲームの起動時に 1 回実行することを想定。
         */
        public function loadGlobalResources(fileNameList:Array, onLoadProgress:Function,
                                            onLoadComplete:Function):void
        {
            var suitablePathList:Array = _mapToSuitablePath(fileNameList);
            _globalScopeAssets.enqueue(suitablePathList);

            _globalScopeAssets.loadQueue(function(loadRatio:Number):void {
                if (onLoadProgress != null) {
                    onLoadProgress(loadRatio);
                }
                if (loadRatio == 1) {
                    onLoadComplete();
                }
            });
        }

        //------------------------------------------------------------
        // getters for game resources
        //------------------------------------------------------------

        /**
         * 拡張子無しのファイル名を指定。シーンスコープのアセットから検索し、
         * 見つからなければグローバルスコープのアセットから検索して返す。
         * （他の getter でも同様）
         */
        public function getImage(fileName:String):Image {
            var texture:Texture = getTexture(fileName);
            if (texture) { return new Image(texture); }

            krew.fwlog('[Error] [KRM] Image not found: ' + fileName);
            return null;
        }

        public function getTexture(fileName:String):Texture {
            var texture:Texture = _sceneScopeAssets.getTexture(fileName);
            if (texture) { return texture; }

            texture = _globalScopeAssets.getTexture(fileName);
            if (texture) { return texture; }

            krew.fwlog('[Error] [KRM] Texture not found: ' + fileName);
            return null;
        }

        public function getSound(fileName:String):Sound {
            var sound:Sound = _sceneScopeAssets.getSound(fileName);
            if (sound) { return sound; }

            sound = _globalScopeAssets.getSound(fileName);
            if (sound) { return sound; }

            krew.fwlog('[Error] [KRM] Sound not found: ' + fileName);
            return null;
        }

        public function getXml(fileName:String):XML {
            var xml:XML = _sceneScopeAssets.getXml(fileName);
            if (xml) { return xml; }

            xml = _globalScopeAssets.getXml(fileName);
            if (xml) { return xml; }

            krew.fwlog('[Error] [KRM] XML not found: ' + fileName);
            return null;
        }

        public function getObject(fileName:String):Object {
            var obj:Object = _sceneScopeAssets.getObject(fileName);
            if (obj) { return obj; }

            obj = _globalScopeAssets.getObject(fileName);
            if (obj) { return obj; }

            krew.fwlog('[Error] [KRM] Object not found: ' + fileName);
            return null;

            return _sceneScopeAssets.getObject(fileName);
        }

        //------------------------------------------------------------
        // private
        //------------------------------------------------------------
        private function _mapToSuitablePath(fileNameList:Array):Array {
            var suitablePathList:Array = [];
            for each (var fileName:String in fileNameList) {
                suitablePathList.push(
                    _urlScheme + KrewConfig.ASSET_BASE_PATH + fileName
                );
            }
            return suitablePathList;
        }

    }
}

package krewfw.core_internal {

    import flash.media.Sound;

    import starling.display.Image;
    import starling.textures.Texture;
    import starling.utils.AssetManager;

    import krewfw.KrewConfig;
    import krewfw.utils.krew;

    //------------------------------------------------------------
    public class KrewResourceManager {

        private var _globalScopeAssets:AssetManager;
        private var _sceneScopeAssets :AssetManager;

        //------------------------------------------------------------
        public function KrewResourceManager() {
            _globalScopeAssets = new KrewConfig.ASSET_MANAGER_CLASS;
            _sceneScopeAssets  = new KrewConfig.ASSET_MANAGER_CLASS;

            _globalScopeAssets.verbose = KrewConfig.ASSET_MANAGER_VERBOSE;
            _sceneScopeAssets .verbose = KrewConfig.ASSET_MANAGER_VERBOSE;
        }

        //------------------------------------------------------------
        // accessor
        //------------------------------------------------------------

        public function get globalAssetManager():AssetManager {
            return _globalScopeAssets;
        }

        public function get sceneAssetManager():AssetManager {
            return _sceneScopeAssets;
        }

        //------------------------------------------------------------

        /**
         * シーン単位で使いたいリソースのロード.
         * 基本的に starling.utils.AssetManager をラップしているだけなので
         * そちらのドキュメントを見よ。
         *
         * <ul>
         *   <li> テクスチャアトラスを読み込む場合は単純に png と xml の 2 つを指定すればよい。
         *        getImage("拡張子を除くファイル名") で取得できる。
         *        画像はファイル名で引くため、全アトラスでソース画像のファイル名（というか xml 内の識別子）
         *        がユニークになっている必要がある
         *   </li>
         *   <li> mp3  はロード後 getSound()  で取得できる </li>
         *   <li> json も読める。 getObject() で取得できる </li>
         *   <li> xml  も読める。 getXml()    で取得できる </li>
         *
         *   <li> ビットマップフォントは png と fnt を読み込み後、自動的に
         *        starling.text.TextField で使用可能になる
         *   </li>
         * </ul>
         */
        public function loadResources(fileNameList:Array, onLoadProgress:Function,
                                      onLoadComplete:Function):void
        {
            var suitablePathList:Array = _mapToSuitablePath(fileNameList);
            _sceneScopeAssets.enqueue(suitablePathList);

            _sceneScopeAssets.loadQueue(function(loadRatio:Number):void {
                if (onLoadProgress != null) {
                    onLoadProgress(loadRatio);
                }
                if (loadRatio == 1) {
                    onLoadComplete();
                }
            });
        }

        /**
         * シーン遷移時にこれを呼んでメモリ解放（テクスチャの dispose を呼ぶ）
         */
        public function purgeSceneScopeResources():void {
            _sceneScopeAssets.purge();
        }

        /**
         * ゲームを通してずっとメモリに保持しておくリソースのロード.
         * （Loading 画面のアセットなど）
         * 使い方は loadResources と同じ。
         * ロードしたものの取得は getGlobalImage() などと Global がついたメソッドで。
         *
         * 現状はゲームの起動時に 1 回実行することを想定。
         */
        public function loadGlobalResources(fileNameList:Array, onLoadProgress:Function,
                                            onLoadComplete:Function):void
        {
            var suitablePathList:Array = _mapToSuitablePath(fileNameList);
            _globalScopeAssets.enqueue(suitablePathList);

            _globalScopeAssets.loadQueue(function(loadRatio:Number):void {
                if (onLoadProgress != null) {
                    onLoadProgress(loadRatio);
                }
                if (loadRatio == 1) {
                    onLoadComplete();
                }
            });
        }

        //------------------------------------------------------------
        // getters for game resources
        //------------------------------------------------------------

        /**
         * 拡張子無しのファイル名を指定。シーンスコープのアセットから検索し、
         * 見つからなければグローバルスコープのアセットから検索して返す。
         * （他の getter でも同様）
         */
        public function getImage(fileName:String):Image {
            var texture:Texture = getTexture(fileName);
            if (texture) { return new Image(texture); }

            krew.fwlog('[Error] [KRM] Image not found: ' + fileName);
            return null;
        }

        public function getTexture(fileName:String):Texture {
            var texture:Texture = _sceneScopeAssets.getTexture(fileName);
            if (texture) { return texture; }

            texture = _globalScopeAssets.getTexture(fileName);
            if (texture) { return texture; }

            krew.fwlog('[Error] [KRM] Texture not found: ' + fileName);
            return null;
        }

        public function getSound(fileName:String):Sound {
            var sound:Sound = _sceneScopeAssets.getSound(fileName);
            if (sound) { return sound; }

            sound = _globalScopeAssets.getSound(fileName);
            if (sound) { return sound; }

            krew.fwlog('[Error] [KRM] Sound not found: ' + fileName);
            return null;
        }

        public function getXml(fileName:String):XML {
            var xml:XML = _sceneScopeAssets.getXml(fileName);
            if (xml) { return xml; }

            xml = _globalScopeAssets.getXml(fileName);
            if (xml) { return xml; }

            krew.fwlog('[Error] [KRM] XML not found: ' + fileName);
            return null;
        }

        public function getObject(fileName:String):Object {
            var obj:Object = _sceneScopeAssets.getObject(fileName);
            if (obj) { return obj; }

            obj = _globalScopeAssets.getObject(fileName);
            if (obj) { return obj; }

            krew.fwlog('[Error] [KRM] Object not found: ' + fileName);
            return null;

            return _sceneScopeAssets.getObject(fileName);
        }

        /**
         * 現環境におけるアセットファイルの URL を取得する
         */
        public function getURL(fileName:String):String {
            return KrewConfig.ASSET_URL_SCHEME + KrewConfig.ASSET_BASE_PATH + fileName;
        }

        //------------------------------------------------------------
        // private
        //------------------------------------------------------------
        private function _mapToSuitablePath(fileNameList:Array):Array {
            var suitablePathList:Array = [];
            for each (var fileName:String in fileNameList) {
                suitablePathList.push(getURL(fileName));
            }
            return suitablePathList;
        }

    }
}

Modify resource manager

Modify resource manager


////////////////////////////////////////////////////////////////////////////////
//
//  Licensed to the Apache Software Foundation (ASF) under one or more
//  contributor license agreements.  See the NOTICE file distributed with
//  this work for additional information regarding copyright ownership.
//  The ASF licenses this file to You under the Apache License, Version 2.0
//  (the "License"); you may not use this file except in compliance with
//  the License.  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////

package spark.skins.ios7
{
	import flash.display.DisplayObject;
	import flash.events.TimerEvent;
	import flash.geom.Matrix;
	import flash.utils.Timer;

	import mx.core.DPIClassification;

	import spark.components.BusyIndicator;
	import spark.skins.ios7.assets.BusyIndicator;
	import spark.skins.mobile.supportClasses.MobileSkin;

	public class BusyIndicatorSkin extends MobileSkin
	{
		static private const DEFAULT_ROTATION_INTERVAL:Number = 30;
		private var busyIndicatorClass:Class;
		private var busyIndicator:DisplayObject;
		private var busyIndicatorBackground:DisplayObject;
		private var busyIndicatorDiameter:Number;
		private var rotationTimer:Timer;
		private var rotationInterval:Number;
		private var rotationSpeed:Number;
		/**
		 *  @private
		 *
		 *  Current rotation of this component in degrees.
		 */
		private var currentRotation:Number = 0;
		private var symbolColor:uint;
		private var symbolColorChanged:Boolean = false;

		public function BusyIndicatorSkin()
		{
			super();

			busyIndicatorClass = spark.skins.ios7.assets.BusyIndicator;
			rotationInterval = getStyle("rotationInterval");
			if (isNaN(rotationInterval))
				rotationInterval = DEFAULT_ROTATION_INTERVAL;
			if (rotationInterval < 30) //Spokes are at 30 degree angle to each other.
				rotationInterval = 30;
			rotationSpeed = 60;

			switch(applicationDPI)
			{
				case DPIClassification.DPI_640:
				{
					busyIndicatorDiameter = 144;
					break;
				}
				case DPIClassification.DPI_480:
				{
					busyIndicatorDiameter = 108;
					break;
				}
				case DPIClassification.DPI_320:
				{
					busyIndicatorDiameter = 72;
					break;
				}
				case DPIClassification.DPI_240:
				{
					busyIndicatorDiameter = 54;
					break;
				}
				case DPIClassification.DPI_120:
				{
					busyIndicatorDiameter = 27;
					break;
				}
				default://160 DPI
				{
					busyIndicatorDiameter = 36;
					break;
				}
			}
		}

		private var _hostComponent:spark.components.BusyIndicator;

		public function get hostComponent():spark.components.BusyIndicator
		{
			return _hostComponent;
		}

		public function set hostComponent(value:spark.components.BusyIndicator):void
		{
			_hostComponent = value;
		}

		override protected function createChildren():void
		{
			//This layer stays still in the background
			busyIndicatorBackground = new busyIndicatorClass();
			busyIndicatorBackground.width = busyIndicatorBackground.height = busyIndicatorDiameter;
			addChild(busyIndicatorBackground);
			//This layer rotates in the foreground to give the required effect
			busyIndicator = new busyIndicatorClass();
			busyIndicator.alpha = 0.3;
			busyIndicator.width = busyIndicator.height = busyIndicatorDiameter;
			addChild(busyIndicator);
		}

		override protected function measure():void
		{
			measuredWidth = busyIndicatorDiameter;
			measuredHeight = busyIndicatorDiameter;

			measuredMinHeight = busyIndicatorDiameter;
			measuredMinWidth = busyIndicatorDiameter
		}

		override protected function commitCurrentState():void
		{
			super.commitCurrentState();
			if(currentState == "rotatingState")
			{
				startRotation();
			}
			else
			{
				stopRotation();
			}
		}

		override public function styleChanged(styleProp:String):void
		{
			var allStyles:Boolean = !styleProp || styleProp == "styleName";

			if (allStyles || styleProp == "symbolColor")
			{
				symbolColor = getStyle("symbolColor");
				symbolColorChanged = true;
				invalidateDisplayList();
			}
			super.styleChanged(styleProp);
		}

		override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
		{
			super.updateDisplayList(unscaledWidth,unscaledHeight);
			if(symbolColorChanged)
			{
				colorizeSymbol();
				symbolColorChanged = false;
			}
		}

		private function colorizeSymbol():void
		{
			super.applyColorTransform(this.busyIndicator, 0x000000, symbolColor);
		}

		private function startRotation():void
		{
			rotationTimer = new Timer(rotationSpeed);
			if (!rotationTimer.hasEventListener(TimerEvent.TIMER))
			{
				rotationTimer.addEventListener(TimerEvent.TIMER, timerHandler);
				rotationTimer.start();
			}
		}

		private function stopRotation():void
		{
			if (rotationTimer)
			{
				rotationTimer.removeEventListener(TimerEvent.TIMER, timerHandler);
				rotationTimer.stop();
				rotationTimer = null;
			}
		}

		/**
		 *  @private
		 *
		 *  Rotate the spinner once for each timer event.
		 */
		private function timerHandler(event:TimerEvent):void
		{
			currentRotation += rotationInterval;
			if (currentRotation >= 360)
				currentRotation = 0;

			rotate(busyIndicator,currentRotation,measuredWidth/2,measuredHeight/2);
			event.updateAfterEvent();
		}

		private var rotationMatrix:Matrix;
		private function rotate(obj:DisplayObject, angle:Number, aroundX:Number, aroundY:Number):void
		{
			rotationMatrix = new Matrix();
			rotationMatrix.translate(-aroundX,-aroundY);
			rotationMatrix.rotate(Math.PI*angle/180);
			rotationMatrix.translate(aroundX,aroundY);
			obj.transform.matrix = rotationMatrix;
		}

	}
}

////////////////////////////////////////////////////////////////////////////////
//
//  Licensed to the Apache Software Foundation (ASF) under one or more
//  contributor license agreements.  See the NOTICE file distributed with
//  this work for additional information regarding copyright ownership.
//  The ASF licenses this file to You under the Apache License, Version 2.0
//  (the "License"); you may not use this file except in compliance with
//  the License.  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////

package spark.skins.ios7
{
	import flash.display.DisplayObject;
	import flash.events.TimerEvent;
	import flash.geom.Matrix;
	import flash.utils.Timer;

	import mx.core.DPIClassification;

	import spark.components.BusyIndicator;
	import spark.skins.ios7.assets.BusyIndicator;
	import spark.skins.mobile.supportClasses.MobileSkin;

	public class BusyIndicatorSkin extends MobileSkin
	{
		static private const DEFAULT_ROTATION_INTERVAL:Number = 30;
		private var busyIndicatorClass:Class;
		private var busyIndicator:DisplayObject;
		private var busyIndicatorBackground:DisplayObject;
		private var busyIndicatorDiameter:Number;
		private var rotationTimer:Timer;
		private var rotationInterval:Number;
		private var rotationSpeed:Number;
		/**
		 *  @private
		 *
		 *  Current rotation of this component in degrees.
		 */
		private var currentRotation:Number = 0;
		private var symbolColor:uint;
		private var symbolColorChanged:Boolean = false;

		public function BusyIndicatorSkin()
		{
			super();

			busyIndicatorClass = spark.skins.ios7.assets.BusyIndicator;
			rotationInterval = getStyle("rotationInterval");
			if (isNaN(rotationInterval))
				rotationInterval = DEFAULT_ROTATION_INTERVAL;
			if (rotationInterval < 30) //Spokes are at 30 degree angle to each other.
				rotationInterval = 30;
			rotationSpeed = 60;

			switch(applicationDPI)
			{
				case DPIClassification.DPI_640:
				{
					busyIndicatorDiameter = 104;
					break;
				}
				case DPIClassification.DPI_480:
				{
					busyIndicatorDiameter = 80;
					break;
				}
				case DPIClassification.DPI_320:
				{
					busyIndicatorDiameter = 52;
					break;
				}
				case DPIClassification.DPI_240:
				{
					busyIndicatorDiameter = 40;
					break;
				}
				case DPIClassification.DPI_120:
				{
					busyIndicatorDiameter = 20;
					break;
				}
				default://160 DPI
				{
					busyIndicatorDiameter = 20;
					break;
				}
			}
		}

		private var _hostComponent:spark.components.BusyIndicator;

		public function get hostComponent():spark.components.BusyIndicator
		{
			return _hostComponent;
		}

		public function set hostComponent(value:spark.components.BusyIndicator):void
		{
			_hostComponent = value;
		}

		override protected function createChildren():void
		{
			//This layer stays still in the background
			busyIndicatorBackground = new busyIndicatorClass();
			busyIndicatorBackground.width = busyIndicatorBackground.height = busyIndicatorDiameter;
			addChild(busyIndicatorBackground);
			//This layer rotates in the foreground to give the required effect
			busyIndicator = new busyIndicatorClass();
			busyIndicator.alpha = 0.3;
			busyIndicator.width = busyIndicator.height = busyIndicatorDiameter;
			addChild(busyIndicator);
		}

		override protected function measure():void
		{
			measuredWidth = busyIndicatorDiameter;
			measuredHeight = busyIndicatorDiameter;

			measuredMinHeight = busyIndicatorDiameter;
			measuredMinWidth = busyIndicatorDiameter
		}

		override protected function commitCurrentState():void
		{
			super.commitCurrentState();
			if(currentState == "rotatingState")
			{
				startRotation();
			}
			else
			{
				stopRotation();
			}
		}

		override public function styleChanged(styleProp:String):void
		{
			var allStyles:Boolean = !styleProp || styleProp == "styleName";

			if (allStyles || styleProp == "symbolColor")
			{
				symbolColor = getStyle("symbolColor");
				symbolColorChanged = true;
				invalidateDisplayList();
			}
			super.styleChanged(styleProp);
		}

		override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
		{
			super.updateDisplayList(unscaledWidth,unscaledHeight);
			if(symbolColorChanged)
			{
				colorizeSymbol();
				symbolColorChanged = false;
			}
		}

		private function colorizeSymbol():void
		{
			super.applyColorTransform(this.busyIndicator, 0x000000, symbolColor);
		}

		private function startRotation():void
		{
			rotationTimer = new Timer(rotationSpeed);
			if (!rotationTimer.hasEventListener(TimerEvent.TIMER))
			{
				rotationTimer.addEventListener(TimerEvent.TIMER, timerHandler);
				rotationTimer.start();
			}
		}

		private function stopRotation():void
		{
			if (rotationTimer)
			{
				rotationTimer.removeEventListener(TimerEvent.TIMER, timerHandler);
				rotationTimer.stop();
				rotationTimer = null;
			}
		}

		/**
		 *  @private
		 *
		 *  Rotate the spinner once for each timer event.
		 */
		private function timerHandler(event:TimerEvent):void
		{
			currentRotation += rotationInterval;
			if (currentRotation >= 360)
				currentRotation = 0;

			rotate(busyIndicator,currentRotation,measuredWidth/2,measuredHeight/2);
			event.updateAfterEvent();
		}

		private var rotationMatrix:Matrix;
		private function rotate(obj:DisplayObject, angle:Number, aroundX:Number, aroundY:Number):void
		{
			rotationMatrix = new Matrix();
			rotationMatrix.translate(-aroundX,-aroundY);
			rotationMatrix.rotate(Math.PI*angle/180);
			rotationMatrix.translate(aroundX,aroundY);
			obj.transform.matrix = rotationMatrix;
		}

	}
}

Use legacy BusyIndicator default sizes (new sizes were breaking Mustella tests)


//
// $Id$
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library 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 GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

package com.threerings.flex {

import flash.utils.Dictionary;

import mx.collections.ArrayCollection;
import mx.collections.Sort;

import mx.containers.VBox;

import mx.core.ClassFactory;
import mx.core.ScrollPolicy;

import com.threerings.util.Comparable;

public class PlayerList extends VBox
{
    /**
     * Create a new player list.
     *
     * @param labelCreator If null, will use a default implementation that does nothing special
     *                     with text formatting or click behavior.
     */
    public function PlayerList (labelCreator :NameLabelCreator = null) :void
    {
        _labelCreator = labelCreator;
        if (_labelCreator == null) {
            _labelCreator = new DefaultNameLabelCreator();
        }

        // set up the UI
        width = 280;
        height = 125;
        _list = new AmbidextrousList();
        _list.verticalScrollPolicy = ScrollPolicy.ON;
        _list.selectable = false;
        _list.percentWidth = 100;
        _list.percentHeight = 100;
        _list.itemRenderer = new ClassFactory(getRenderingClass());
        _list.dataProvider = _players;

        addChild(_list);

        // set up the sort for the collection
        var sort :Sort = new Sort();
        sort.compareFunction = sortFunction;
        _players.sort = sort;
    }

    /**
     * The PlayerList is meant to include data that is at least as complicated as a Name, so to
     * keep things simple and extendable, we require Comparable.  This allows the issue of passing
     * the NameLabelCreator into the player renderer to be kept simple and straightforward and still
     * allow efficient item updating.
     */
    public function addItem (value :Comparable) :void
    {
        var currentValue :Array = _values[value] as Array;
        if (currentValue != null) {
            currentValue[1] = value;
            // this is the same array already contained in the list, so the proper renderer should
            // be notified of the new value.
            _players.itemUpdated(currentValue);
        } else {
            currentValue = [_labelCreator, value];
            _values[value] = currentValue;
            _players.addItem(currentValue);
        }
    }

    public function removeItem (value :Comparable) :void
    {
        var currentValue :Array = _values[value] as Array;
        if (currentValue != null) {
            _players.removeItemAt(_players.getItemIndex(currentValue));
        }

        delete _values[value];
    }

    /**
     * Notify the list that this value has changed internally, and the renderer should be told to
     * redraw its contents.
     */
    public function itemUpdated (value :Comparable) :void
    {
        var currentValue :Array = _values[value] as Array;
        if (currentValue != null) {
            currentValue[1] = value;
            _players.itemUpdated(currentValue);
        }
    }

    protected function getRenderingClass () :Class
    {
        return PlayerRenderer;
    }

    protected function sortFunction (o1 :Object, o2 :Object, fields :Array = null) :int
    {
        if (!(o1 is Array) || !(o2 is Array)) {
            return 0;
        }

        var data1 :Object = (o1 as Array)[1];
        var data2 :Object = (o2 as Array)[1];
        if (data1 is Comparable && data2 is Comparable) {
            return (data1 as Comparable).compareTo(data2);
        } else {
            // default to actionscript's magical great than or less than operators
            return data1 > data2 ? -1 : (data1 < data2 ? 1 : 0);
        }
    }

    protected var _labelCreator :NameLabelCreator;
    protected var _list :AmbidextrousList;
    protected var _players :ArrayCollection = new ArrayCollection();
    protected var _values :Dictionary = new Dictionary();
}
}

import mx.containers.HBox;

import mx.controls.Label;

import mx.core.ScrollPolicy;
import mx.core.UIComponent;

import com.threerings.flex.NameLabelCreator;

import com.threerings.util.Name;

class DefaultNameLabelCreator implements NameLabelCreator
{
    public function createLabel (name :Name) :UIComponent
    {
        var label :Label = new Label();
        label.text = "" + name;
        return label;
    }
}

/**
 * A renederer for lists that contain Names.
 */
class PlayerRenderer extends HBox
{
    public function PlayerRenderer ()
    {
        super();

        verticalScrollPolicy = ScrollPolicy.OFF;
        horizontalScrollPolicy = ScrollPolicy.OFF;
        // the horizontalGap should be 8...
    }

    override public function set data (value :Object) :void
    {
        super.data = value;

        if (processedDescriptors) {
            configureUI();
        }
    }

    override protected function createChildren () :void
    {
        super.createChildren();

        configureUI();
    }

    /**
     * Update the UI elements with the data we're displaying.
     */
    protected function configureUI () :void
    {
        removeAllChildren();

        if (this.data != null && (this.data is Array) && (this.data as Array).length == 2) {
            var dataArray :Array = this.data as Array;
            var creator :NameLabelCreator = dataArray[0] as NameLabelCreator;
            var name :Name = dataArray[1] as Name;
            if (creator != null && name != null) {
                addChild(creator.createLabel(name));
            }
        }
    }
}

//
// $Id$
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library 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 GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

package com.threerings.flex {

import flash.utils.Dictionary;

import mx.collections.ArrayCollection;
import mx.collections.Sort;

import mx.containers.VBox;

import mx.core.ClassFactory;
import mx.core.ScrollPolicy;

import com.threerings.util.Comparable;

public class PlayerList extends VBox
{
    /**
     * Create a new player list.
     *
     * @param labelCreator If null, will use a default implementation that does nothing special
     *                     with text formatting or click behavior.
     */
    public function PlayerList (labelCreator :NameLabelCreator = null) :void
    {
        _labelCreator = labelCreator;
        if (_labelCreator == null) {
            _labelCreator = new DefaultNameLabelCreator();
        }

        // set up the UI
        width = 280;
        height = 125;
        _list = new AmbidextrousList();
        _list.verticalScrollPolicy = ScrollPolicy.ON;
        _list.selectable = false;
        _list.percentWidth = 100;
        _list.percentHeight = 100;
        _list.itemRenderer = new ClassFactory(getRenderingClass());
        _list.dataProvider = _players;

        addChild(_list);

        // set up the sort for the collection
        var sort :Sort = new Sort();
        sort.compareFunction = sortFunction;
        _players.sort = sort;
    }

    public function clear () :void
    {
        _values = new Dictionary();
        _players.removeAll();
    }

    /**
     * The PlayerList is meant to include data that is at least as complicated as a Name, so to
     * keep things simple and extendable, we require Comparable.  This allows the issue of passing
     * the NameLabelCreator into the player renderer to be kept simple and straightforward and still
     * allow efficient item updating.
     */
    public function addItem (value :Comparable) :void
    {
        var currentValue :Array = _values[value] as Array;
        if (currentValue != null) {
            currentValue[1] = value;
            // this is the same array already contained in the list, so the proper renderer should
            // be notified of the new value.
            _players.itemUpdated(currentValue);
        } else {
            currentValue = [_labelCreator, value];
            _values[value] = currentValue;
            _players.addItem(currentValue);
        }
    }

    public function removeItem (value :Comparable) :void
    {
        var currentValue :Array = _values[value] as Array;
        if (currentValue != null) {
            _players.removeItemAt(_players.getItemIndex(currentValue));
        }

        delete _values[value];
    }

    /**
     * Notify the list that this value has changed internally, and the renderer should be told to
     * redraw its contents.
     */
    public function itemUpdated (value :Comparable) :void
    {
        var currentValue :Array = _values[value] as Array;
        if (currentValue != null) {
            currentValue[1] = value;
            _players.itemUpdated(currentValue);
        }
    }

    protected function getRenderingClass () :Class
    {
        return PlayerRenderer;
    }

    protected function sortFunction (o1 :Object, o2 :Object, fields :Array = null) :int
    {
        if (!(o1 is Array) || !(o2 is Array)) {
            return 0;
        }

        var data1 :Object = (o1 as Array)[1];
        var data2 :Object = (o2 as Array)[1];
        if (data1 is Comparable && data2 is Comparable) {
            return (data1 as Comparable).compareTo(data2);
        } else {
            // default to actionscript's magical great than or less than operators
            return data1 > data2 ? -1 : (data1 < data2 ? 1 : 0);
        }
    }

    protected var _labelCreator :NameLabelCreator;
    protected var _list :AmbidextrousList;
    protected var _players :ArrayCollection = new ArrayCollection();
    protected var _values :Dictionary = new Dictionary();
}
}

import mx.containers.HBox;

import mx.controls.Label;

import mx.core.ScrollPolicy;
import mx.core.UIComponent;

import com.threerings.flex.NameLabelCreator;

import com.threerings.util.Name;

class DefaultNameLabelCreator implements NameLabelCreator
{
    public function createLabel (name :Name) :UIComponent
    {
        var label :Label = new Label();
        label.text = "" + name;
        return label;
    }
}

/**
 * A renederer for lists that contain Names.
 */
class PlayerRenderer extends HBox
{
    public function PlayerRenderer ()
    {
        super();

        verticalScrollPolicy = ScrollPolicy.OFF;
        horizontalScrollPolicy = ScrollPolicy.OFF;
        // the horizontalGap should be 8...
    }

    override public function set data (value :Object) :void
    {
        super.data = value;

        if (processedDescriptors) {
            configureUI();
        }
    }

    override protected function createChildren () :void
    {
        super.createChildren();

        configureUI();
    }

    /**
     * Update the UI elements with the data we're displaying.
     */
    protected function configureUI () :void
    {
        removeAllChildren();

        if (this.data != null && (this.data is Array) && (this.data as Array).length == 2) {
            var dataArray :Array = this.data as Array;
            var creator :NameLabelCreator = dataArray[0] as NameLabelCreator;
            var name :Name = dataArray[1] as Name;
            if (creator != null && name != null) {
                addChild(creator.createLabel(name));
            }
        }
    }
}

Add clear() functionality

Add clear() functionality

git-svn-id: b675b909355d5cf946977f44a8ec5a6ceb3782e4@482 ed5b42cb-e716-0410-a449-f6a68f950b19


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 package org.mangui.osmf.plugins {
    import org.mangui.hls.utils.Params2Settings;
    import org.mangui.osmf.plugins.loader.HLSLoaderBase;
    import org.mangui.osmf.plugins.loader.HLSLoadFromDocumentElement;
    import org.osmf.media.MediaElement;
    import org.osmf.media.MediaFactoryItem;
    import org.osmf.media.MediaFactoryItemType;
    import org.osmf.media.MediaResourceBase;
    import org.osmf.media.PluginInfo;

    CONFIG::LOGGING {
    import org.mangui.hls.utils.Log;
    }

    public class HLSPlugin extends PluginInfo {
        public function HLSPlugin(items : Vector.<MediaFactoryItem>=null, elementCreatedNotification : Function = null) {
            items = new Vector.<MediaFactoryItem>();
            items.push(new MediaFactoryItem('org.mangui.osmf.plugins.HLSPlugin', canHandleResource, createMediaElement, MediaFactoryItemType.STANDARD));

            super(items, elementCreatedNotification);
        }

        /**
         * Called from super class when plugin has been initialized with the MediaFactory from which it was loaded.
         * Used for customize HLSSettings with values provided in resource metadata (that was set eg. in flash vars)
         *
         * @param resource  Provides acces to the resource used to load the plugin and any associated metadata
         *
         */
        override public function initializePlugin(resource : MediaResourceBase) : void {
            CONFIG::LOGGING {
            Log.debug("OSMF HLSPlugin init");
            }
            metadataParamsToHLSSettings(resource);
        }

        private function canHandleResource(resource : MediaResourceBase) : Boolean {
            return HLSLoaderBase.canHandle(resource);
        }

        private function createMediaElement() : MediaElement {
            return new HLSLoadFromDocumentElement(null, new HLSLoaderBase());
        }

        private function metadataParamsToHLSSettings(resource : MediaResourceBase) : void {
            if (resource == null) {
                return;
            }

            var metadataNamespaceURLs : Vector.<String> = resource.metadataNamespaceURLs;

            // set all legal params values to HLSSetings properties
            for each (var key : String in metadataNamespaceURLs) {
                Params2Settings.set(key, resource.getMetadataValue(key));
            }
        }
    }
}

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 package org.mangui.osmf.plugins {
    import org.mangui.hls.utils.Params2Settings;
    import org.mangui.osmf.plugins.loader.HLSLoaderBase;
    import org.mangui.osmf.plugins.loader.HLSLoadFromDocumentElement;
    import org.osmf.media.MediaElement;
    import org.osmf.media.MediaFactoryItem;
    import org.osmf.media.MediaFactoryItemType;
    import org.osmf.media.MediaResourceBase;
    import org.osmf.media.PluginInfo;

    CONFIG::LOGGING {
    import org.mangui.hls.utils.Log;
    }

    public class HLSPlugin extends PluginInfo {
        public function HLSPlugin(items : Vector.<MediaFactoryItem>=null, elementCreatedNotification : Function = null) {
            items = new Vector.<MediaFactoryItem>();
            items.push(new MediaFactoryItem('org.mangui.osmf.plugins.HLSPlugin', canHandleResource, createMediaElement, MediaFactoryItemType.STANDARD));

            super(items, elementCreatedNotification);
        }

        /**
         * Called from super class when plugin has been initialized with the MediaFactory from which it was loaded.
         * Used for customize HLSSettings with values provided in resource metadata (that was set eg. in flash vars)
         *
         * @param resource  Provides access to the resource used to load the plugin and any associated metadata
         *
         */
        override public function initializePlugin(resource : MediaResourceBase) : void {
            CONFIG::LOGGING {
            Log.debug("OSMF HLSPlugin init");
            }
            metadataParamsToHLSSettings(resource);
        }

        private function canHandleResource(resource : MediaResourceBase) : Boolean {
            return HLSLoaderBase.canHandle(resource);
        }

        private function createMediaElement() : MediaElement {
            return new HLSLoadFromDocumentElement(null, new HLSLoaderBase());
        }

        private function metadataParamsToHLSSettings(resource : MediaResourceBase) : void {
            if (resource == null) {
                return;
            }

            var metadataNamespaceURLs : Vector.<String> = resource.metadataNamespaceURLs;

            // set all legal params values to HLSSettings properties
            for each (var key : String in metadataNamespaceURLs) {
                Params2Settings.set(key, resource.getMetadataValue(key));
            }
        }
    }
}

Fix a couple of typos in comments.

Fix a couple of typos in comments.


package {

import com.marstonstudio.crossusermedia.encoder.Encoder;

import flash.display.DisplayObjectContainer;

import flash.events.Event;
import flash.utils.ByteArray;
import flash.utils.getTimer;

import mx.core.ByteArrayAsset;
import mx.core.UIComponent;

import org.flexunit.asserts.assertNotNull;

import org.flexunit.asserts.assertTrue;
import org.flexunit.async.Async;
import org.fluint.uiImpersonation.UIImpersonator;

public class TestApp {

        public function TestApp() {}

        [Embed(source="../resources/audio.pcm",mimeType="application/octet-stream")]
        public var AudioPcm:Class;

        public var container:DisplayObjectContainer;

        [Before(async,ui)]
        public function initialize():void {
            trace("+++++++++++++++++++++++++++++++++++++++++++++++++++++++");
            trace("test::start " + new Date().toLocaleString());
            trace("");
            container = new UIComponent();
            Async.proceedOnEvent(this, container, Event.ADDED_TO_STAGE, 100);
            UIImpersonator.addChild(container);
        }

        [Test(description="Load audio asset")]
        public function testAudioLoad():void {

            var audioPcmAsset:ByteArrayAsset = new AudioPcm();
            assertTrue("embedded bytesAvailable", audioPcmAsset.bytesAvailable > 0);

            assertNotNull(container.stage);

            const sampleRate:int = 16000;
            const format:String = 'f32be';
            const bitRate:int = 32000;

            var encoder:Encoder = new Encoder(container);
            encoder.init(format, sampleRate, format, sampleRate, bitRate);
            encoder.load(audioPcmAsset);
            var output:ByteArray = encoder.flush();

            assertTrue("outputSampleRate set to " + sampleRate, encoder.getOutputSampleRate() == sampleRate);
            assertTrue("outputFormat set to " + format, encoder.getOutputFormat() == format);
            assertTrue("outputLength = audioPcmAsset.length = " + audioPcmAsset.length, encoder.getOutputLength() == audioPcmAsset.length);
            assertTrue("output.length = audioPcmAsset.length = " + audioPcmAsset.length, output.length == audioPcmAsset.length);

            //give stdout buffer a chance to catch up before terminating
            sleep(1000);

            encoder.dispose(0);
        }

        [After]
        public function finalize():void {
            UIImpersonator.removeAllChildren();
            container = null;

            trace("");
            trace("test::complete " + new Date().toLocaleString());
            trace("=======================================================");
        }

        private function sleep(ms:int):void {
            var init:int = getTimer();
            while(true) {
                if(getTimer() - init >= ms) {
                    break;
                }
            }
        }

    }
}

package {

import com.marstonstudio.crossusermedia.encoder.Encoder;

import flash.display.DisplayObjectContainer;

import flash.events.Event;
import flash.utils.ByteArray;
import flash.utils.getTimer;

import mx.core.ByteArrayAsset;
import mx.core.UIComponent;

import org.flexunit.asserts.assertNotNull;

import org.flexunit.asserts.assertTrue;
import org.flexunit.async.Async;
import org.fluint.uiImpersonation.UIImpersonator;

public class TestApp {

        public function TestApp() {}

        [Embed(source="../resources/audio.pcm",mimeType="application/octet-stream")]
        public var AudioPcm:Class;

        public var container:DisplayObjectContainer;

        [Before(async,ui)]
        public function initialize():void {
            trace("+++++++++++++++++++++++++++++++++++++++++++++++++++++++");
            trace("test::start " + new Date().toLocaleString());
            trace("");
            container = new UIComponent();
            Async.proceedOnEvent(this, container, Event.ADDED_TO_STAGE, 100);
            UIImpersonator.addChild(container);
        }

        [Test(description="Load audio asset")]
        public function testAudioLoad():void {

            var audioPcmAsset:ByteArrayAsset = new AudioPcm();
            assertTrue("embedded bytesAvailable", audioPcmAsset.bytesAvailable > 0);

            assertNotNull(container.stage);

            const sampleRate:int = 16000;
            const format:String = 'f32be';
            const bitRate:int = 32000;

            var encoder:Encoder = new Encoder(container);
            encoder.init(format, sampleRate, format, sampleRate, bitRate);
            encoder.load(audioPcmAsset);
            var output:ByteArray = encoder.flush();

            assertTrue("outputSampleRate set to " + sampleRate, encoder.getOutputSampleRate() == sampleRate);
            assertTrue("outputFormat set to " + format, encoder.getOutputFormat() == format);
            assertTrue("outputLength = audioPcmAsset.length = " + audioPcmAsset.length, encoder.getOutputLength() == audioPcmAsset.length);
            assertTrue("output.length = audioPcmAsset.length = " + audioPcmAsset.length, output.length == audioPcmAsset.length);

            encoder.dispose(0);
        }

        [After(ui)]
        public function finalize():void {
            UIImpersonator.removeAllChildren();
            container = null;

            trace("");
            trace("test::complete " + new Date().toLocaleString());
            trace("=======================================================");
            sleep(1000);
        }

        private function sleep(ms:int):void {
            var init:int = getTimer();
            while(true) {
                if(getTimer() - init >= ms) {
                    break;
                }
            }
        }

    }
}

add sleep to test for console output

add sleep to test for console output


package aerys.minko.render.geometry.primitive
{
	import aerys.minko.render.geometry.Geometry;
	import aerys.minko.render.geometry.stream.IVertexStream;
	import aerys.minko.render.geometry.stream.IndexStream;
	import aerys.minko.render.geometry.stream.VertexStream;
	import aerys.minko.render.geometry.stream.format.VertexFormat;

	public class QuadGeometry extends Geometry
	{
		private static var _instance	: QuadGeometry	= null;

		public static function get quadGeometry() : QuadGeometry
		{
			return _instance || (_instance = new QuadGeometry());
		}

		public function QuadGeometry(doubleSided	: Boolean 	= false,
									 numColumns 	: uint 		= 1,
									 numRows 		: uint 		= 0,
									 streamsUsage	: uint		= 0)
		{
			var vertices 	: Vector.<Number> 	= new Vector.<Number>();
			var indices 	: Vector.<uint> 	= new Vector.<uint>();

			numRows ||= numColumns;

			for (var y : int = 0; y <= numRows; y++)
			{
				for (var x : int = 0; x <= numColumns; x++)
				{
					vertices.push(
						x / numColumns - .5, y / numRows - .5, 0.,
						x / numColumns, 1. - y / numRows
					);
				}
			}

			for (y = 0; y < numRows; y++)
			{
				for (x = 0; x < numColumns; x++)
				{
					indices.push(
						x + (numColumns + 1) * y,
						x + 1 + y * (numColumns + 1),
						(y + 1) * (numColumns + 1) + x,
						x + 1 + y * (numColumns + 1),
						(y + 1) * (numColumns + 1) + x + 1,
						(y + 1) * (numColumns + 1) + x
					);
				}
			}

			if (doubleSided)
				indices = indices.concat(indices.concat().reverse());

			var vstream : VertexStream = new VertexStream(
				streamsUsage,
				VertexFormat.XYZ_UV,
				vertices
			);

			super(
				new <IVertexStream>[vstream],
				new IndexStream(streamsUsage, indices)
			);
		}

	}
}

package aerys.minko.render.geometry.primitive
{
	import aerys.minko.render.geometry.Geometry;
	import aerys.minko.render.geometry.stream.IVertexStream;
	import aerys.minko.render.geometry.stream.IndexStream;
	import aerys.minko.render.geometry.stream.VertexStream;
	import aerys.minko.render.geometry.stream.format.VertexFormat;

	public class QuadGeometry extends Geometry
	{
		private static var _instance			: QuadGeometry	= null;
		private static var _instanceDoubleSided	: QuadGeometry	= null;

		public static function get quadGeometry() : QuadGeometry
		{
			return _instance || (_instance = new QuadGeometry());
		}

		public static function get doubleSidedQuadGeometry() : QuadGeometry
		{
			return _instanceDoubleSided || (_instanceDoubleSided = new QuadGeometry(true));
		}

		public function QuadGeometry(doubleSided	: Boolean 	= false,
									 numColumns 	: uint 		= 1,
									 numRows 		: uint 		= 0,
									 streamsUsage	: uint		= 0)
		{
			var vertices 	: Vector.<Number> 	= new Vector.<Number>();
			var indices 	: Vector.<uint> 	= new Vector.<uint>();

			numRows ||= numColumns;

			for (var y : int = 0; y <= numRows; y++)
			{
				for (var x : int = 0; x <= numColumns; x++)
				{
					vertices.push(
						x / numColumns - .5, y / numRows - .5, 0.,
						x / numColumns, 1. - y / numRows
					);
				}
			}

			for (y = 0; y < numRows; y++)
			{
				for (x = 0; x < numColumns; x++)
				{
					indices.push(
						x + (numColumns + 1) * y,
						x + 1 + y * (numColumns + 1),
						(y + 1) * (numColumns + 1) + x,
						x + 1 + y * (numColumns + 1),
						(y + 1) * (numColumns + 1) + x + 1,
						(y + 1) * (numColumns + 1) + x
					);
				}
			}

			if (doubleSided)
				indices = indices.concat(indices.concat().reverse());

			var vstream : VertexStream = new VertexStream(
				streamsUsage,
				VertexFormat.XYZ_UV,
				vertices
			);

			super(
				new <IVertexStream>[vstream],
				new IndexStream(streamsUsage, indices)
			);
		}

	}
}

add QuadGeometry.doubleSidedQuadGeometry


////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2008-2013 Esri. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
////////////////////////////////////////////////////////////////////////////////
package com.esri.builder.views.supportClasses
{

import com.esri.builder.supportClasses.URLUtil;

import flashx.textLayout.conversion.TextConverter;
import flashx.textLayout.elements.Configuration;
import flashx.textLayout.elements.IConfiguration;
import flashx.textLayout.elements.TextFlow;
import flashx.textLayout.formats.TextDecoration;
import flashx.textLayout.formats.TextLayoutFormat;

import mx.utils.StringUtil;

public class TextFlowUtil
{
    public static function toURLFlow(url:String, label:String = null):TextFlow
    {
        if (!label)
        {
            label = url;
        }

        var urlSource:String =
            StringUtil.substitute("{1}", URLUtil.encode(url), label);

        return TextConverter.importToFlow(urlSource,
                                          TextConverter.TEXT_FIELD_HTML_FORMAT,
                                          getURLConfig());
    }

    private static function getURLConfig():IConfiguration
    {
        var config:Configuration = TextFlow.defaultConfiguration;

        var activeFormat:TextLayoutFormat = new TextLayoutFormat(config.defaultLinkActiveFormat);
        activeFormat.color = 0x000000;
        activeFormat.textDecoration = TextDecoration.UNDERLINE;

        var hoverFormat:TextLayoutFormat = new TextLayoutFormat(config.defaultLinkHoverFormat);
        hoverFormat.color = 0x000000;
        hoverFormat.textDecoration = TextDecoration.UNDERLINE;

        var normalFormat:TextLayoutFormat = new TextLayoutFormat(config.defaultLinkNormalFormat);
        normalFormat.color = 0x007AC2;
        normalFormat.textDecoration = TextDecoration.UNDERLINE;

        config.defaultLinkActiveFormat = activeFormat;
        config.defaultLinkHoverFormat = hoverFormat;
        config.defaultLinkNormalFormat = normalFormat;

        return config;
    }
}
}

////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2008-2013 Esri. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
////////////////////////////////////////////////////////////////////////////////
package com.esri.builder.views.supportClasses
{

import com.esri.builder.supportClasses.URLUtil;

import flashx.textLayout.conversion.TextConverter;
import flashx.textLayout.elements.Configuration;
import flashx.textLayout.elements.IConfiguration;
import flashx.textLayout.elements.TextFlow;
import flashx.textLayout.formats.TextDecoration;
import flashx.textLayout.formats.TextLayoutFormat;

import mx.utils.StringUtil;

public class TextFlowUtil
{
    public static function toURLFlow(url:String, label:String = null):TextFlow
    {
        if (!label)
        {
            label = url;
        }

        var urlSource:String =
            StringUtil.substitute("{1}", URLUtil.decode(url), label);

        return TextConverter.importToFlow(urlSource,
                                          TextConverter.TEXT_FIELD_HTML_FORMAT,
                                          getURLConfig());
    }

    private static function getURLConfig():IConfiguration
    {
        var config:Configuration = TextFlow.defaultConfiguration;

        var activeFormat:TextLayoutFormat = new TextLayoutFormat(config.defaultLinkActiveFormat);
        activeFormat.color = 0x000000;
        activeFormat.textDecoration = TextDecoration.UNDERLINE;

        var hoverFormat:TextLayoutFormat = new TextLayoutFormat(config.defaultLinkHoverFormat);
        hoverFormat.color = 0x000000;
        hoverFormat.textDecoration = TextDecoration.UNDERLINE;

        var normalFormat:TextLayoutFormat = new TextLayoutFormat(config.defaultLinkNormalFormat);
        normalFormat.color = 0x007AC2;
        normalFormat.textDecoration = TextDecoration.UNDERLINE;

        config.defaultLinkActiveFormat = activeFormat;
        config.defaultLinkHoverFormat = hoverFormat;
        config.defaultLinkNormalFormat = normalFormat;

        return config;
    }
}
}

Fix double-encoding caused by encoding URL TextFlow.


package org.denivip.osmf.elements.m3u8Classes
{
	import flash.events.EventDispatcher;

	import org.denivip.osmf.net.HLSDynamicStreamingResource;
	import org.osmf.events.ParseEvent;
	import org.osmf.logging.Log;
	import org.osmf.logging.Logger;
	import org.osmf.media.MediaResourceBase;
	import org.osmf.media.MediaType;
	import org.osmf.media.URLResource;
	import org.osmf.metadata.Metadata;
	import org.osmf.metadata.MetadataNamespaces;
	import org.osmf.net.DynamicStreamingItem;
	import org.osmf.net.DynamicStreamingResource;
	import org.osmf.net.StreamType;
	import org.osmf.net.StreamingItem;
	import org.osmf.net.StreamingURLResource;

	[Event(name="parseComplete", type="org.osmf.events.ParseEvent")]
	[Event(name="parseError", type="org.osmf.events.ParseEvent")]

	/**
	 * You will not belive, but this class parses *.m3u8 playlist
	 */
	public class M3U8PlaylistParser extends EventDispatcher
	{
		public function M3U8PlaylistParser(){
			// nothing todo here...
		}

		public function parse(value:String, baseResource:URLResource):void{

			if(!value || value == ''){
				throw new ArgumentError("Parsed value is missing =(");
			}

			var lines:Array = value.split(/\r?\n/);

			if(lines[0] != '#EXTM3U'){
				;
				CONFIG::LOGGING
				{
					logger.warn('Incorrect header! {0}', lines[0]);
				}
			}

			var result:MediaResourceBase;
			var isLive:Boolean = true;
			var streamItems:Vector.<DynamicStreamingItem>;
			var tempStreamingRes:StreamingURLResource = null;
			var tempDynamicRes:DynamicStreamingResource = null;
			var alternateStreams:Vector.<StreamingItem> = null;
			for(var i:int = 1; i < lines.length; i++){
				var line:String = String(lines[i]).replace(/^([\s|\t|\n]+)?(.*)([\s|\t|\n]+)?$/gm, "$2");

				if(line.indexOf("#EXTINF:") == 0){
					result = baseResource;
					tempStreamingRes = result as StreamingURLResource;

					if(tempStreamingRes && tempStreamingRes.streamType == StreamType.LIVE_OR_RECORDED){
						for(var j:int = i+1; j < lines.length; j++){
							if(String(lines[j]).indexOf('#EXT-X-ENDLIST') == 0){
								isLive = false;
								break;
							}
						}
						if(isLive)
							tempStreamingRes.streamType = StreamType.LIVE;
					}
					break;
				}

				if(line.indexOf("#EXT-X-STREAM-INF:") == 0){
					if(!result){
						result = new HLSDynamicStreamingResource(baseResource.url);
						tempDynamicRes = result as DynamicStreamingResource;
						tempStreamingRes = baseResource as StreamingURLResource;
						if(tempStreamingRes){
							tempDynamicRes.streamType = tempStreamingRes.streamType;
							tempDynamicRes.clipStartTime = tempStreamingRes.clipStartTime;
							tempDynamicRes.clipEndTime = tempStreamingRes.clipEndTime;
						}
						streamItems = new Vector.<DynamicStreamingItem>();
					}

					var bw:Number;
					if(line.search(/BANDWIDTH=(\d+)/) > 0)
						bw = parseFloat(line.match(/BANDWIDTH=(\d+)/)[1])/1000;

					var width:int = -1;
					var height:int = -1;
					if(line.search(/RESOLUTION=(\d+)x(\d+)/) > 0){
						width = parseInt(line.match(/RESOLUTION=(\d+)x(\d+)/)[1]);
						height = parseInt(line.match(/RESOLUTION=(\d+)x(\d+)/)[2]);
					}

					var name:String = lines[i+1];
					streamItems.push(new DynamicStreamingItem(name, bw, width, height));

					DynamicStreamingResource(result).streamItems = streamItems;
				}

				if(line.indexOf("#EXT-X-MEDIA:") == 0){
					if(line.search(/TYPE=(.*?)\W/) > 0 && line.match(/TYPE=(.*?)\W/)[1] == 'AUDIO'){
						var stUrl:String;
						var lang:String;
						var stName:String;
						if(line.search(/URI="(.*?)"/) > 0){
							stUrl = line.match(/URI="(.*?)"/)[1];
							if(stUrl.search(/(file|https?):\/\//) != 0){
								stUrl = baseResource.url.substr(0, baseResource.url.lastIndexOf('/')+1) + stUrl;
							}
						}
						if(line.search(/LANGUAGE="(.*?)"/) > 0){
							lang = line.match(/LANGUAGE="(.*?)"/)[1]
						}
						if(line.search(/NAME="(.*?)"/) > 0){
							stName = line.match(/NAME="(.*?)"/)[1];
						}
						if(!alternateStreams)
							alternateStreams = new Vector.<StreamingItem>();

						alternateStreams.push(
							new StreamingItem(MediaType.AUDIO, stUrl, 0, {label:stName, language:lang})
						);
					}
				}
			}

			if(tempDynamicRes && tempDynamicRes.streamItems){
				if(tempDynamicRes.streamItems.length == 1){
					tempStreamingRes = baseResource as StreamingURLResource;
					if(tempStreamingRes){
						var url:String = tempDynamicRes.host.substr(0, tempDynamicRes.host.lastIndexOf('/')+1) + tempDynamicRes.streamItems[0].streamName;
						result = new StreamingURLResource(
							url,
							tempStreamingRes.streamType,
							tempStreamingRes.clipStartTime,
							tempStreamingRes.clipEndTime,
							tempStreamingRes.connectionArguments,
							tempStreamingRes.urlIncludesFMSApplicationInstance,
							tempStreamingRes.drmContentData
						);
					}
				}else if(baseResource.getMetadataValue(MetadataNamespaces.RESOURCE_INITIAL_INDEX) != null){
					var initialIndex:int = baseResource.getMetadataValue(MetadataNamespaces.RESOURCE_INITIAL_INDEX) as int;
					tempDynamicRes.initialIndex = initialIndex < 0 ? 0 : (initialIndex >= tempDynamicRes.streamItems.length) ? (tempDynamicRes.streamItems.length-1) : initialIndex;
				}
			}

			baseResource.addMetadataValue("HLS_METADATA", new Metadata()); // fix for multistreaming resources
			result.addMetadataValue("HLS_METADATA", new Metadata());

			var httpMetadata:Metadata = new Metadata();
			result.addMetadataValue(MetadataNamespaces.HTTP_STREAMING_METADATA, httpMetadata);

			if(alternateStreams && result is StreamingURLResource){
				(result as StreamingURLResource).alternativeAudioStreamItems = alternateStreams;
			}

			if(result is StreamingURLResource && StreamingURLResource(result).streamType == StreamType.DVR){
				var dvrMetadata:Metadata = new Metadata();
				result.addMetadataValue(MetadataNamespaces.DVR_METADATA, dvrMetadata);
			}

			dispatchEvent(new ParseEvent(ParseEvent.PARSE_COMPLETE, false, false, result));
		}
		/*
		private function addDVRInfo():void{

			CONFIG::LOGGING
			{
				logger.info('DVR!!! \\o/'); // we happy!
			}

			// black magic...
			var dvrInfo:DVRInfo = new DVRInfo();
			dvrInfo.id = dvrInfo.url = '';
			dvrInfo.isRecording = true; // if live then in process
			//dvrInfo.startTime = 0.0;
			// attach info into playlist
		}
		*/
		CONFIG::LOGGING
		{
			private var logger:Logger = Log.getLogger("org.denivip.osmf.elements.m3u8Classes.M3U8PlaylistParser");
		}
	}
}

package org.denivip.osmf.elements.m3u8Classes
{
	import flash.events.EventDispatcher;

	import org.denivip.osmf.net.HLSDynamicStreamingResource;
	import org.osmf.events.ParseEvent;
	import org.osmf.logging.Log;
	import org.osmf.logging.Logger;
	import org.osmf.media.MediaResourceBase;
	import org.osmf.media.MediaType;
	import org.osmf.media.URLResource;
	import org.osmf.metadata.Metadata;
	import org.osmf.metadata.MetadataNamespaces;
	import org.osmf.net.DynamicStreamingItem;
	import org.osmf.net.DynamicStreamingResource;
	import org.osmf.net.StreamType;
	import org.osmf.net.StreamingItem;
	import org.osmf.net.StreamingURLResource;

	[Event(name="parseComplete", type="org.osmf.events.ParseEvent")]
	[Event(name="parseError", type="org.osmf.events.ParseEvent")]

	/**
	 * You will not belive, but this class parses *.m3u8 playlist
	 */
	public class M3U8PlaylistParser extends EventDispatcher
	{
		public function M3U8PlaylistParser(){
			// nothing todo here...
		}

		public function parse(value:String, baseResource:URLResource):void{

			if(!value || value == ''){
				throw new ArgumentError("Parsed value is missing =(");
			}

			var lines:Array = value.split(/\r?\n/);

			if(lines[0] != '#EXTM3U'){
				;
				CONFIG::LOGGING
				{
					logger.warn('Incorrect header! {0}', lines[0]);
				}
			}

			var result:MediaResourceBase;
			var isLive:Boolean = true;
			var streamItems:Vector.<DynamicStreamingItem>;
			var tempStreamingRes:StreamingURLResource = null;
			var tempDynamicRes:DynamicStreamingResource = null;
			var alternateStreams:Vector.<StreamingItem> = null;
			var initialStreamName:String = '';
			for(var i:int = 1; i < lines.length; i++){
				var line:String = String(lines[i]).replace(/^([\s|\t|\n]+)?(.*)([\s|\t|\n]+)?$/gm, "$2");

				if(line.indexOf("#EXTINF:") == 0){
					result = baseResource;
					tempStreamingRes = result as StreamingURLResource;

					if(tempStreamingRes && tempStreamingRes.streamType == StreamType.LIVE_OR_RECORDED){
						for(var j:int = i+1; j < lines.length; j++){
							if(String(lines[j]).indexOf('#EXT-X-ENDLIST') == 0){
								isLive = false;
								break;
							}
						}
						if(isLive)
							tempStreamingRes.streamType = StreamType.LIVE;
					}
					break;
				}

				if(line.indexOf("#EXT-X-STREAM-INF:") == 0){
					if(!result){
						result = new HLSDynamicStreamingResource(baseResource.url);
						tempDynamicRes = result as DynamicStreamingResource;
						tempStreamingRes = baseResource as StreamingURLResource;
						if(tempStreamingRes){
							tempDynamicRes.streamType = tempStreamingRes.streamType;
							tempDynamicRes.clipStartTime = tempStreamingRes.clipStartTime;
							tempDynamicRes.clipEndTime = tempStreamingRes.clipEndTime;
						}
						streamItems = new Vector.<DynamicStreamingItem>();
					}

					var bw:Number;
					if(line.search(/BANDWIDTH=(\d+)/) > 0)
						bw = parseFloat(line.match(/BANDWIDTH=(\d+)/)[1])/1000;

					var width:int = -1;
					var height:int = -1;
					if(line.search(/RESOLUTION=(\d+)x(\d+)/) > 0){
						width = parseInt(line.match(/RESOLUTION=(\d+)x(\d+)/)[1]);
						height = parseInt(line.match(/RESOLUTION=(\d+)x(\d+)/)[2]);
					}

					var name:String = lines[i+1];
					streamItems.push(new DynamicStreamingItem(name, bw, width, height));
					// store stream name of first stream encountered
					if(initialStreamName == ''){
						initialStreamName = name;
					}
					DynamicStreamingResource(result).streamItems = streamItems;
				}

				if(line.indexOf("#EXT-X-MEDIA:") == 0){
					if(line.search(/TYPE=(.*?)\W/) > 0 && line.match(/TYPE=(.*?)\W/)[1] == 'AUDIO'){
						var stUrl:String;
						var lang:String;
						var stName:String;
						if(line.search(/URI="(.*?)"/) > 0){
							stUrl = line.match(/URI="(.*?)"/)[1];
							if(stUrl.search(/(file|https?):\/\//) != 0){
								stUrl = baseResource.url.substr(0, baseResource.url.lastIndexOf('/')+1) + stUrl;
							}
						}
						if(line.search(/LANGUAGE="(.*?)"/) > 0){
							lang = line.match(/LANGUAGE="(.*?)"/)[1]
						}
						if(line.search(/NAME="(.*?)"/) > 0){
							stName = line.match(/NAME="(.*?)"/)[1];
						}
						if(!alternateStreams)
							alternateStreams = new Vector.<StreamingItem>();

						alternateStreams.push(
							new StreamingItem(MediaType.AUDIO, stUrl, 0, {label:stName, language:lang})
						);
					}
				}
			}

			if(tempDynamicRes && tempDynamicRes.streamItems){
				if(tempDynamicRes.streamItems.length == 1){
					tempStreamingRes = baseResource as StreamingURLResource;
					if(tempStreamingRes){
						var url:String = tempDynamicRes.host.substr(0, tempDynamicRes.host.lastIndexOf('/')+1) + tempDynamicRes.streamItems[0].streamName;
						result = new StreamingURLResource(
							url,
							tempStreamingRes.streamType,
							tempStreamingRes.clipStartTime,
							tempStreamingRes.clipEndTime,
							tempStreamingRes.connectionArguments,
							tempStreamingRes.urlIncludesFMSApplicationInstance,
							tempStreamingRes.drmContentData
						);
					}
				}else{
					if(baseResource.getMetadataValue(MetadataNamespaces.RESOURCE_INITIAL_INDEX) != null){
						var initialIndex:int = baseResource.getMetadataValue(MetadataNamespaces.RESOURCE_INITIAL_INDEX) as int;
						tempDynamicRes.initialIndex = initialIndex < 0 ? 0 : (initialIndex >= tempDynamicRes.streamItems.length) ? (tempDynamicRes.streamItems.length-1) : initialIndex;
					}else{
						// set initialIndex to index of first stream name encountered
						tempDynamicRes.initialIndex = tempDynamicRes.indexFromName(initialStreamName);
					}
				}
			}

			baseResource.addMetadataValue("HLS_METADATA", new Metadata()); // fix for multistreaming resources
			result.addMetadataValue("HLS_METADATA", new Metadata());

			var httpMetadata:Metadata = new Metadata();
			result.addMetadataValue(MetadataNamespaces.HTTP_STREAMING_METADATA, httpMetadata);

			if(alternateStreams && result is StreamingURLResource){
				(result as StreamingURLResource).alternativeAudioStreamItems = alternateStreams;
			}

			if(result is StreamingURLResource && StreamingURLResource(result).streamType == StreamType.DVR){
				var dvrMetadata:Metadata = new Metadata();
				result.addMetadataValue(MetadataNamespaces.DVR_METADATA, dvrMetadata);
			}

			dispatchEvent(new ParseEvent(ParseEvent.PARSE_COMPLETE, false, false, result));
		}
		/*
		private function addDVRInfo():void{

			CONFIG::LOGGING
			{
				logger.info('DVR!!! \\o/'); // we happy!
			}

			// black magic...
			var dvrInfo:DVRInfo = new DVRInfo();
			dvrInfo.id = dvrInfo.url = '';
			dvrInfo.isRecording = true; // if live then in process
			//dvrInfo.startTime = 0.0;
			// attach info into playlist
		}
		*/
		CONFIG::LOGGING
		{
			private var logger:Logger = Log.getLogger("org.denivip.osmf.elements.m3u8Classes.M3U8PlaylistParser");
		}
	}
}

Set initial stream to be played to first entry in M3U8 playlist

According to HLS especficiation we should play initially the first item
in the m3u8 playlist:
"The first entry in the variant playlist is played when a user joins
the stream and is used as part of a test to determine which stream is
most appropriate. The order of the other entries is irrelevant."
from:
https://developer.apple.com/library/mac/documentation/networkinginternet
/conceptual/streamingmediaguide/UsingHTTPLiveStreaming/UsingHTTPLiveStre
aming.html#//apple_ref/doc/uid/TP40008332-CH102-SW18


;
; P3JS
; Write some assembly code.
; Then click 'Assemble and Run' to run it on the P3 simulator.
;

; Write some assembly code here.
; Then click 'Assemble and Run' to run it on the P3 simulator.

; For more information check 'About P3JS'.

; Try the Demos ----->  ----->  ----->  ----->  ----->  ----->  ----->  ----->

; This is a very short program that fills the memory with ac5fh!
; Click 'Assemble and Run' to test.

                ORIG    8000h

                MOV     R1, M[PC]
                MOV     M[PC], R1

; How does it work? Why is the value at the address 8000h not ac5fh?

; HINT
;  ||
;  ||
;  \/

; HINT: Notice that ac5fh is the binary code for 'MOV M[PC], R1'.

Update welcome.as

Update welcome.as


//
// $Id$

package com.threerings.util {

import flash.events.EventDispatcher;
import flash.events.TimerEvent;

import flash.utils.getTimer; // function import
import flash.utils.Timer;

/**
 * Dispatched when a set element expires.
 *
 * @eventType com.threerings.util.ExpiringSet.ELEMENT_EXPIRED
 */
[Event(name="ElementExpired", type="com.threerings.util.ValueEvent")]

/**
 * Data structure that keeps its elements for a short time, and then removes them automatically.
 *
 * All operations are O(n), including add().
 */
public class ExpiringSet extends EventDispatcher
    implements Set
{
    /** The even that is dispatched when a member of this set expires. */
    public static const ELEMENT_EXPIRED :String = "ElementExpired";

    /**
     * Initializes the expiring set.
     *
     * @param ttl Time to live value for set elements, in seconds.
     */
    public function ExpiringSet (ttl :Number)
    {
        _ttl = Math.round(ttl * 1000);
        _timer = new Timer(_ttl, 1);
        _timer.addEventListener(TimerEvent.TIMER, checkTimer);
    }

    /**
     * Returns the time to live value for this ExpiringSet.  This value cannot be changed after
     * set creation.
     */
    public function get ttl () :Number
    {
        return _ttl / 1000;
    }

    /**
     * Calling this function will not expire the elements, it simply removes them. No
     * ValueEvent will be dispatched.
     */
    public function clear () :void
    {
        // simply trunate the data array
        _data.length = 0;
    }

    // from Set
    public function forEach (fn :Function) :void
    {
        for each (var e :ExpiringElement in _data) {
            fn(e.element);
        }
    }

    // from Set
    public function size () :int
    {
        return _data.length;
    }

    // from Set
    public function isEmpty () :Boolean
    {
        return size() == 0;
    }

    // from Set
    public function contains (o :Object) :Boolean
    {
        return _data.some(function (e :ExpiringElement, ... ignored) :Boolean {
            return e.objectEquals(o);
        });
    }

    /**
     * Note that if you add an object that the list already contains, this method will return
     * false, but it will also update the expire time on that object to be this sets ttl from now,
     * as if the item really were being added to the list now.
     */
    public function add (o :Object) :Boolean
    {
        var added :Boolean = true;
        for (var ii :int = 0; ii < _data.length; ii++) {
            if (ExpiringElement(_data[ii]).objectEquals(o)) {
                // already contained, remove this one and re-add at end
                _data.splice(ii, 1);
                added = false;
                break;
            }
        }

        // push the item onto the queue. since each element has the same TTL, elements end up
        // being ordered by their expiration time.
        _data.push(new ExpiringElement(o, getTimer() + _ttl));
        if (_data.length == 1) {
            // set up the timer to remove this one element, otherwise it was already running
            _timer.reset();
            _timer.delay = _ttl;
            _timer.start();
        }
        return added;
    }

    // from Set
    public function remove (o :Object) :Boolean
    {
        // pull the item from anywhere in the queue.  If we remove the first element, the timer
        // will harmlessly NOOP when it wakes up
        for (var ii :int = 0; ii < _data.length; ii++) {
            if (ExpiringElement(_data[ii]).objectEquals(o)) {
                _data.splice(ii, 1);
                return true;
            }
        }
        return false;
    }

    /**
     * This implementation of Set returns a fresh array that it will never reference again.
     * Modification of this array will not change the ExpiringSet's structure.
     */
    public function toArray () :Array
    {
        return _data.map(function (e :ExpiringElement, ... ignored) :Object {
            return e.element;
        });
    }

    /**
     * Remove probably just one element from the front of the expiration queue, and
     * schedule a wakeup for the time to the new head's expiration time.
     */
    protected function checkTimer (... ignored) :void
    {
        var now :int = getTimer();
        while (_data.length > 0) {
            var e :ExpiringElement = ExpiringElement(_data[0]);
            var timeToExpire :int = e.expirationTime - now;
            if (timeToExpire <= 0) {
                _data.shift(); // remove it
                dispatchEvent(new ValueEvent(ELEMENT_EXPIRED, e.element)); // notify

            } else {
                // the head element is not yet expired
                _timer.reset();
                _timer.delay = timeToExpire;
                _timer.start();
                break;
            }
        }
    }

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

    /** The time to live for this set, not to be changed after construction. */
    protected /* final */ var _ttl :int;

    /** Array of ExpiringElement instances, sorted by expiration time. */
    protected var _data :Array = [];

    protected var _timer :Timer;
}
}

import com.threerings.util.Util;

class ExpiringElement
{
    public var expirationTime :int;
    public var element :Object;

    public function ExpiringElement (element :Object, expiration :int)
    {
        this.element = element;
        this.expirationTime = expiration;
    }

    public function objectEquals (element :Object) :Boolean
    {
        return Util.equals(element, this.element);
    }
}

//
// $Id$

package com.threerings.util {

import flash.events.EventDispatcher;
import flash.events.TimerEvent;

import flash.utils.getTimer; // function import
import flash.utils.Timer;

/**
 * Dispatched when a set element expires.
 *
 * @eventType com.threerings.util.ExpiringSet.ELEMENT_EXPIRED
 */
[Event(name="ElementExpired", type="com.threerings.util.ValueEvent")]

/**
 * Data structure that keeps its elements for a short time, and then removes them automatically.
 *
 * All operations are O(n), including add().
 */
public class ExpiringSet extends EventDispatcher
    implements Set
{
    /** The even that is dispatched when a member of this set expires. */
    public static const ELEMENT_EXPIRED :String = "ElementExpired";

    /**
     * Initializes the expiring set.
     *
     * @param ttl Time to live value for set elements, in seconds.
     * @param expireHandler a function to be conveniently registered as an event listener.
     */
    public function ExpiringSet (ttl :Number, expireHandler :Function = null)
    {
        _ttl = Math.round(ttl * 1000);
        _timer = new Timer(_ttl, 1);
        _timer.addEventListener(TimerEvent.TIMER, checkTimer);

        if (expireHandler != null) {
            addEventListener(ELEMENT_EXPIRED, expireHandler);
        }
    }

    /**
     * Returns the time to live value for this ExpiringSet.  This value cannot be changed after
     * set creation.
     */
    public function get ttl () :Number
    {
        return _ttl / 1000;
    }

    /**
     * Calling this function will not expire the elements, it simply removes them. No
     * ValueEvent will be dispatched.
     */
    public function clear () :void
    {
        // simply trunate the data array
        _data.length = 0;
    }

    // from Set
    public function forEach (fn :Function) :void
    {
        for each (var e :ExpiringElement in _data) {
            fn(e.element);
        }
    }

    // from Set
    public function size () :int
    {
        return _data.length;
    }

    // from Set
    public function isEmpty () :Boolean
    {
        return size() == 0;
    }

    // from Set
    public function contains (o :Object) :Boolean
    {
        return _data.some(function (e :ExpiringElement, ... ignored) :Boolean {
            return e.objectEquals(o);
        });
    }

    /**
     * Note that if you add an object that the list already contains, this method will return
     * false, but it will also update the expire time on that object to be this sets ttl from now,
     * as if the item really were being added to the list now.
     */
    public function add (o :Object) :Boolean
    {
        var added :Boolean = true;
        for (var ii :int = 0; ii < _data.length; ii++) {
            if (ExpiringElement(_data[ii]).objectEquals(o)) {
                // already contained, remove this one and re-add at end
                _data.splice(ii, 1);
                added = false;
                break;
            }
        }

        // push the item onto the queue. since each element has the same TTL, elements end up
        // being ordered by their expiration time.
        _data.push(new ExpiringElement(o, getTimer() + _ttl));
        if (_data.length == 1) {
            // set up the timer to remove this one element, otherwise it was already running
            _timer.reset();
            _timer.delay = _ttl;
            _timer.start();
        }
        return added;
    }

    // from Set
    public function remove (o :Object) :Boolean
    {
        // pull the item from anywhere in the queue.  If we remove the first element, the timer
        // will harmlessly NOOP when it wakes up
        for (var ii :int = 0; ii < _data.length; ii++) {
            if (ExpiringElement(_data[ii]).objectEquals(o)) {
                _data.splice(ii, 1);
                return true;
            }
        }
        return false;
    }

    /**
     * This implementation of Set returns a fresh array that it will never reference again.
     * Modification of this array will not change the ExpiringSet's structure.
     */
    public function toArray () :Array
    {
        return _data.map(function (e :ExpiringElement, ... ignored) :Object {
            return e.element;
        });
    }

    /**
     * Remove probably just one element from the front of the expiration queue, and
     * schedule a wakeup for the time to the new head's expiration time.
     */
    protected function checkTimer (... ignored) :void
    {
        var now :int = getTimer();
        while (_data.length > 0) {
            var e :ExpiringElement = ExpiringElement(_data[0]);
            var timeToExpire :int = e.expirationTime - now;
            if (timeToExpire <= 0) {
                _data.shift(); // remove it
                dispatchEvent(new ValueEvent(ELEMENT_EXPIRED, e.element)); // notify

            } else {
                // the head element is not yet expired
                _timer.reset();
                _timer.delay = timeToExpire;
                _timer.start();
                break;
            }
        }
    }

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

    /** The time to live for this set, not to be changed after construction. */
    protected /* final */ var _ttl :int;

    /** Array of ExpiringElement instances, sorted by expiration time. */
    protected var _data :Array = [];

    protected var _timer :Timer;
}
}

import com.threerings.util.Util;

class ExpiringElement
{
    public var expirationTime :int;
    public var element :Object;

    public function ExpiringElement (element :Object, expiration :int)
    {
        this.element = element;
        this.expirationTime = expiration;
    }

    public function objectEquals (element :Object) :Boolean
    {
        return Util.equals(element, this.element);
    }
}

Allow the event handler to be specified in the constructor.

git-svn-id: a1a4b28b82a3276cc491891159dd9963a0a72fae@5783 542714f4-19e9-0310-aa3c-eee0fc999fb1


package aerys.minko.render.material.phong
{
	import aerys.minko.render.RenderTarget;
	import aerys.minko.render.material.basic.BasicProperties;
	import aerys.minko.render.shader.SFloat;
	import aerys.minko.render.shader.Shader;
	import aerys.minko.render.shader.ShaderSettings;
	import aerys.minko.render.shader.part.DiffuseShaderPart;
	import aerys.minko.render.shader.part.animation.VertexAnimationShaderPart;
	import aerys.minko.scene.data.LightDataProvider;
	import aerys.minko.scene.node.light.SpotLight;
	import aerys.minko.type.enum.Blending;
	import aerys.minko.type.enum.TriangleCulling;

	public class PCFShadowMapShader extends Shader
	{
		private var _vertexAnimationPart	: VertexAnimationShaderPart;
		private var _diffusePart			: DiffuseShaderPart;

		private var _lightId				: uint;
		private var _clipspacePosition		: SFloat;

		public function PCFShadowMapShader(lightId		: uint,
											  priority		: Number,
											  renderTarget	: RenderTarget)
		{
			super(renderTarget, priority);

			_lightId = lightId;

			_vertexAnimationPart = new VertexAnimationShaderPart(this);
			_diffusePart = new DiffuseShaderPart(this);
		}

		override protected function initializeSettings(settings : ShaderSettings) : void
		{
			super.initializeSettings(settings);

			settings.blending = Blending.NORMAL;
			settings.enabled = meshBindings.getProperty(PhongProperties.CAST_SHADOWS, true);
			settings.triangleCulling = meshBindings.getProperty(
                BasicProperties.TRIANGLE_CULLING, TriangleCulling.BACK
            );
		}

		override protected function getVertexPosition() : SFloat
		{
			var lightTypeName		: String = LightDataProvider.getLightPropertyName(
                'type', _lightId
            );
			var worldToScreenName	: String = LightDataProvider.getLightPropertyName(
                'worldToScreen', _lightId
            );

			var lightType			: uint	 = sceneBindings.getProperty(lightTypeName);
			var worldToScreen		: SFloat = sceneBindings.getParameter(worldToScreenName, 16);
			var vertexPosition		: SFloat = localToWorld(
                _vertexAnimationPart.getAnimatedVertexPosition()
            );

			_clipspacePosition = multiply4x4(vertexPosition, worldToScreen);

			if (lightType == SpotLight.LIGHT_TYPE)
				return float4(
                    _clipspacePosition.xy,
                    multiply(_clipspacePosition.z, _clipspacePosition.w),
                    _clipspacePosition.w
                );
			else
				return _clipspacePosition;
		}

		/**
		 * @see http://www.mvps.org/directx/articles/linear_z/linearz.htm Linear Z-buffering
		 */
		override protected function getPixelColor() : SFloat
		{
			var iClipspacePosition	: SFloat = interpolate(_clipspacePosition);

			if (meshBindings.propertyExists(BasicProperties.ALPHA_THRESHOLD))
			{
				var diffuse			: SFloat	= _diffusePart.getDiffuseColor();
				var alphaThreshold 	: SFloat 	= meshBindings.getParameter(
					BasicProperties.ALPHA_THRESHOLD, 1
				);

				kill(subtract(0.5, lessThan(diffuse.w, alphaThreshold)));
			}

			return pack(iClipspacePosition.z);
		}
	}
}

package aerys.minko.render.material.phong
{
	import aerys.minko.render.RenderTarget;
	import aerys.minko.render.material.basic.BasicProperties;
	import aerys.minko.render.shader.SFloat;
	import aerys.minko.render.shader.Shader;
	import aerys.minko.render.shader.ShaderSettings;
	import aerys.minko.render.shader.part.DiffuseShaderPart;
	import aerys.minko.render.shader.part.animation.VertexAnimationShaderPart;
	import aerys.minko.scene.data.LightDataProvider;
	import aerys.minko.scene.node.light.SpotLight;
	import aerys.minko.type.enum.Blending;
	import aerys.minko.type.enum.TriangleCulling;

	public class PCFShadowMapShader extends Shader
	{
		private var _vertexAnimationPart	: VertexAnimationShaderPart;
		private var _diffusePart			: DiffuseShaderPart;

		private var _lightId				: uint;
		private var _clipspacePosition		: SFloat;

		public function PCFShadowMapShader(lightId		: uint,
										   priority		: Number,
										   renderTarget	: RenderTarget)
		{
			super(renderTarget, priority);

			_lightId = lightId;

			_vertexAnimationPart = new VertexAnimationShaderPart(this);
			_diffusePart = new DiffuseShaderPart(this);
		}

		override protected function initializeSettings(settings : ShaderSettings) : void
		{
			super.initializeSettings(settings);

			settings.blending = Blending.NORMAL;
			settings.triangleCulling = meshBindings.getProperty(
                BasicProperties.TRIANGLE_CULLING, TriangleCulling.BACK
            );
		}

		override protected function getVertexPosition() : SFloat
		{
			var lightTypeName		: String = LightDataProvider.getLightPropertyName(
                'type', _lightId
            );
			var worldToScreenName	: String = LightDataProvider.getLightPropertyName(
                'worldToScreen', _lightId
            );

			var lightType			: uint	 = sceneBindings.getProperty(lightTypeName);
			var worldToScreen		: SFloat = sceneBindings.getParameter(worldToScreenName, 16);
			var vertexPosition		: SFloat = localToWorld(
                _vertexAnimationPart.getAnimatedVertexPosition()
            );

			_clipspacePosition = multiply4x4(vertexPosition, worldToScreen);

			if (lightType == SpotLight.LIGHT_TYPE)
				return float4(
                    _clipspacePosition.xy,
                    multiply(_clipspacePosition.z, _clipspacePosition.w),
                    _clipspacePosition.w
                );
			else
				return _clipspacePosition;
		}

		/**
		 * @see http://www.mvps.org/directx/articles/linear_z/linearz.htm Linear Z-buffering
		 */
		override protected function getPixelColor() : SFloat
		{
			var iClipspacePosition	: SFloat = interpolate(_clipspacePosition);

			if (meshBindings.propertyExists(BasicProperties.ALPHA_THRESHOLD))
			{
				var diffuse			: SFloat	= _diffusePart.getDiffuseColor();
				var alphaThreshold 	: SFloat 	= meshBindings.getParameter(
					BasicProperties.ALPHA_THRESHOLD, 1
				);

				kill(subtract(0.5, lessThan(diffuse.w, alphaThreshold)));
			}

			return pack(iClipspacePosition.z);
		}
	}
}

remove usage of PhongProperties.CAST_SHADOW to set ShaderSettings.enabled in PCFShadowMapShader because the PhongEffect will fork according to this very property and this pass simply won't exist


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 package org.mangui.osmf.plugins.traits {
    import org.mangui.hls.HLS;
    import org.mangui.hls.event.HLSEvent;
    import org.osmf.traits.DynamicStreamTrait;
    import org.osmf.utils.OSMFStrings;

    CONFIG::LOGGING {
    import org.mangui.hls.utils.Log;
    }

    public class HLSDynamicStreamTrait extends DynamicStreamTrait {
        private var _hls : HLS;

        public function HLSDynamicStreamTrait(hls : HLS) {
            CONFIG::LOGGING {
            Log.debug("HLSDynamicStreamTrait()");
            }
            _hls = hls;
            _hls.addEventListener(HLSEvent.LEVEL_SWITCH, _levelSwitchHandler);
            super(true, _hls.startLevel, hls.levels.length);
        }

        override public function dispose() : void {
            CONFIG::LOGGING {
            Log.debug("HLSDynamicStreamTrait:dispose");
            }
            _hls.removeEventListener(HLSEvent.LEVEL_SWITCH, _levelSwitchHandler);
            super.dispose();
        }

        override public function getBitrateForIndex(index : int) : Number {
            if (index > numDynamicStreams - 1 || index < 0) {
                throw new RangeError(OSMFStrings.getString(OSMFStrings.STREAMSWITCH_INVALID_INDEX));
            }
            var bitrate : Number = _hls.levels[index].bitrate / 1000;
            CONFIG::LOGGING {
            Log.debug("HLSDynamicStreamTrait:getBitrateForIndex(" + index + ")=" + bitrate);
            }
            return bitrate;
        }

        override public function switchTo(index : int) : void {
            CONFIG::LOGGING {
            Log.debug("HLSDynamicStreamTrait:switchTo(" + index + ")/max:" + maxAllowedIndex);
            }
            if (index < 0 || index > maxAllowedIndex) {
                throw new RangeError(OSMFStrings.getString(OSMFStrings.STREAMSWITCH_INVALID_INDEX));
            }
            autoSwitch = false;
            if (!switching) {
                setSwitching(true, index);
            }
        }

        override protected function autoSwitchChangeStart(value : Boolean) : void {
            CONFIG::LOGGING {
            Log.debug("HLSDynamicStreamTrait:autoSwitchChangeStart:" + value);
            }
            if (value == true && _hls.autoLevel == false) {
                _hls.level = -1;
                // only seek if position is set
                if (!isNaN(_hls.position)) {
                    _hls.stream.seek(_hls.position);
                }
            }
        }

        override protected function switchingChangeStart(newSwitching : Boolean, index : int) : void {
            CONFIG::LOGGING {
            Log.debug("HLSDynamicStreamTrait:switchingChangeStart(newSwitching/index):" + newSwitching + "/" + index);
            }
            if (newSwitching) {
                _hls.level = index;
            }
        }

        /** Update playback position/duration **/
        private function _levelSwitchHandler(event : HLSEvent) : void {
            var newLevel : int = event.level;
            CONFIG::LOGGING {
            Log.debug("HLSDynamicStreamTrait:_qualitySwitchHandler:" + newLevel);
            }
            setCurrentIndex(newLevel);
            setSwitching(false, newLevel);
        };
    }
}

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 package org.mangui.osmf.plugins.traits {
    import org.mangui.hls.HLS;
    import org.mangui.hls.event.HLSEvent;
    import org.osmf.traits.DynamicStreamTrait;
    import org.osmf.utils.OSMFStrings;

    CONFIG::LOGGING {
    import org.mangui.hls.utils.Log;
    }

    public class HLSDynamicStreamTrait extends DynamicStreamTrait {
        private var _hls : HLS;

        public function HLSDynamicStreamTrait(hls : HLS) {
            CONFIG::LOGGING {
            Log.debug("HLSDynamicStreamTrait()");
            }
            _hls = hls;
            _hls.addEventListener(HLSEvent.LEVEL_SWITCH, _levelSwitchHandler);
            super(true, _hls.startLevel, hls.levels.length);
        }

        override public function dispose() : void {
            CONFIG::LOGGING {
            Log.debug("HLSDynamicStreamTrait:dispose");
            }
            _hls.removeEventListener(HLSEvent.LEVEL_SWITCH, _levelSwitchHandler);
            super.dispose();
        }

        override public function getBitrateForIndex(index : int) : Number {
            if (index > numDynamicStreams - 1 || index < 0) {
                throw new RangeError(OSMFStrings.getString(OSMFStrings.STREAMSWITCH_INVALID_INDEX));
            }
            var bitrate : Number = _hls.levels[index].bitrate / 1000;
            CONFIG::LOGGING {
            Log.debug("HLSDynamicStreamTrait:getBitrateForIndex(" + index + ")=" + bitrate);
            }
            return bitrate;
        }

        override public function switchTo(index : int) : void {
            CONFIG::LOGGING {
            Log.debug("HLSDynamicStreamTrait:switchTo(" + index + ")/max:" + maxAllowedIndex);
            }
            if (index < 0 || index > maxAllowedIndex) {
                throw new RangeError(OSMFStrings.getString(OSMFStrings.STREAMSWITCH_INVALID_INDEX));
            }
            autoSwitch = false;
            if (!switching) {
                setSwitching(true, index);
            }
        }

        override protected function autoSwitchChangeStart(value : Boolean) : void {
            CONFIG::LOGGING {
            Log.debug("HLSDynamicStreamTrait:autoSwitchChangeStart:" + value);
            }
            if (value == true && _hls.autoLevel == false) {
                _hls.loadLevel = -1;
                // only seek if position is set
                if (!isNaN(_hls.position)) {
                    _hls.stream.seek(_hls.position);
                }
            }
        }

        override protected function switchingChangeStart(newSwitching : Boolean, index : int) : void {
            CONFIG::LOGGING {
            Log.debug("HLSDynamicStreamTrait:switchingChangeStart(newSwitching/index):" + newSwitching + "/" + index);
            }
            if (newSwitching) {
                _hls.loadLevel = index;
            }
        }

        /** Update playback position/duration **/
        private function _levelSwitchHandler(event : HLSEvent) : void {
            var newLevel : int = event.level;
            CONFIG::LOGGING {
            Log.debug("HLSDynamicStreamTrait:_qualitySwitchHandler:" + newLevel);
            }
            setCurrentIndex(newLevel);
            setSwitching(false, newLevel);
        };
    }
}

fix compil error

fix compil error