/*
    Weave (Web-based Analysis and Visualization Environment)
    Copyright (C) 2008-2011 University of Massachusetts Lowell

    This file is a part of Weave.

    Weave is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License, Version 3,
    as published by the Free Software Foundation.

    Weave 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Weave.  If not, see <http://www.gnu.org/licenses/>.
*/

package weave.visualization.plotters
{
	import flash.display.BitmapData;
	import flash.display.Graphics;
	import flash.display.Shape;
	import flash.geom.Point;

	import mx.controls.Alert;

	import weave.Weave;
	import weave.api.data.IAttributeColumn;
	import weave.api.data.IQualifiedKey;
	import weave.api.linkSessionState;
	import weave.api.primitives.IBounds2D;
	import weave.data.AttributeColumns.DynamicColumn;
	import weave.data.AttributeColumns.EquationColumn;
	import weave.data.AttributeColumns.FilteredColumn;
	import weave.data.AttributeColumns.SortedColumn;
	import weave.primitives.Bounds2D;
	import weave.utils.BitmapText;
	import weave.utils.ColumnUtils;
	import weave.visualization.plotters.styles.DynamicFillStyle;
	import weave.visualization.plotters.styles.DynamicLineStyle;
	import weave.visualization.plotters.styles.SolidFillStyle;
	import weave.visualization.plotters.styles.SolidLineStyle;

	/**
	 * PieChartPlotter
	 *
	 * @author adufilie
	 */
	public class PieChartPlotter extends AbstractPlotter
	{
		public function PieChartPlotter()
		{
			init();
		}

		private var _beginRadians:EquationColumn;
		private var _spanRadians:EquationColumn;
		private var _filteredData:FilteredColumn;
		private var _labelColumn:DynamicColumn = new DynamicColumn();

		public function get data():DynamicColumn { return _filteredData.internalDynamicColumn; }
		public function get label():DynamicColumn {return _labelColumn;}

		public const lineStyle:DynamicLineStyle = new DynamicLineStyle(SolidLineStyle);
		public const fillStyle:DynamicFillStyle = new DynamicFillStyle(SolidFillStyle);

		private function init():void
		{
			var fill:SolidFillStyle = fillStyle.internalObject as SolidFillStyle;
			fill.color.internalDynamicColumn.globalName = Weave.DEFAULT_COLOR_COLUMN;

			_beginRadians = new EquationColumn();
			_beginRadians.equation.value = "0.5 * PI + getRunningTotal(spanRadians) - getNumber(spanRadians)";
			_spanRadians = _beginRadians.requestVariable("spanRadians", EquationColumn, true);
			_spanRadians.equation.value = "getNumber(sortedData) / getSum(sortedData) * 2 * PI";
			var sortedData:SortedColumn = _spanRadians.requestVariable("sortedData", SortedColumn, true);
			_filteredData = sortedData.internalDynamicColumn.requestLocalObject(FilteredColumn, true);
			linkSessionState(keySet.keyFilter, _filteredData.filter);

			registerSpatialProperties(data);
			registerNonSpatialProperties(fillStyle, lineStyle, label);
			setKeySource(_filteredData);
		}

		override protected function addRecordGraphicsToTempShape(recordKey:IQualifiedKey, dataBounds:IBounds2D, screenBounds:IBounds2D, tempShape:Shape):void
		{
			// project data coordinates to screen coordinates and draw graphics
			var beginRadians:Number = _beginRadians.getValueFromKey(recordKey, Number);
			var spanRadians:Number = _spanRadians.getValueFromKey(recordKey, Number);

			var graphics:Graphics = tempShape.graphics;
			// begin line & fill
			lineStyle.beginLineStyle(recordKey, graphics);
			fillStyle.beginFillStyle(recordKey, graphics);
			// move to center point
			WedgePlotter.drawProjectedWedge(graphics, dataBounds, screenBounds, beginRadians, spanRadians);
			// end fill
			graphics.endFill();
		}

		override public function drawBackground(dataBounds:IBounds2D, screenBounds:IBounds2D, destination:BitmapData):void
		{
			if (_labelColumn.keys.length == 0) return;

			var recordKey:IQualifiedKey;
			var beginRadians:Number;
			var spanRadians:Number;
			var midRadians:Number;
			var xScreenRadius:Number;
			var yScreenRadius:Number;
			var coordinate:Point = new Point();

			for (var i:int ; i < _filteredData.keys.length ; i++)
			{
				if (_labelColumn.containsKey(_filteredData.keys[i] as IQualifiedKey) == false) return;
				recordKey = _filteredData.keys[i] as IQualifiedKey;
				beginRadians = _beginRadians.getValueFromKey(recordKey, Number) as Number;
				spanRadians = _spanRadians.getValueFromKey(recordKey, Number) as Number;
				midRadians = beginRadians + (spanRadians / 2);

				coordinate.x = Math.cos(midRadians);
				coordinate.y = Math.sin(midRadians);
				dataBounds.projectPointTo(coordinate, screenBounds);
				coordinate.x += Math.cos(midRadians) * 10;
				coordinate.y -= Math.sin(midRadians) * 10;
				var labelText:BitmapText = new BitmapText();
				labelText.text = ("  " + _labelColumn.getValueFromKey((_filteredData.keys[i] as IQualifiedKey)) + "  ");
				if (midRadians < (Math.PI / 2) || midRadians > ((3 * Math.PI) / 2))
				{
					labelText.horizontalAlign = BitmapText.HORIZONTAL_ALIGN_LEFT;
					labelText.verticalAlign = BitmapText.VERTICAL_ALIGN_CENTER;
				}
				else if (midRadians == ((3 * Math.PI) / 2))
				{
					labelText.horizontalAlign = BitmapText.HORIZONTAL_ALIGN_CENTER;
				}
				else if (midRadians == (Math.PI / 2))
				{
					labelText.horizontalAlign = BitmapText.HORIZONTAL_ALIGN_CENTER ;
					labelText.verticalAlign = BitmapText.VERTICAL_ALIGN_BOTTOM ;
				}
				else
				{
					labelText.horizontalAlign = BitmapText.HORIZONTAL_ALIGN_RIGHT;
					labelText.verticalAlign = BitmapText.VERTICAL_ALIGN_CENTER;
				}

				labelText.textFormat.color=Weave.properties.axisFontColor.value;
				labelText.textFormat.size=Weave.properties.axisFontSize.value;
				labelText.textFormat.underline=Weave.properties.axisFontUnderline.value;
				labelText.x = coordinate.x;
				labelText.y = coordinate.y;
				labelText.draw(destination);
			}
		}

		/**
		 * This gets the data bounds of the bin that a record key falls into.
		 */
		override public function getDataBoundsFromRecordKey(recordKey:IQualifiedKey):Array
		{
			var beginRadians:Number = _beginRadians.getValueFromKey(recordKey, Number);
			var spanRadians:Number = _spanRadians.getValueFromKey(recordKey, Number);
			var bounds:IBounds2D = getReusableBounds();
			WedgePlotter.getWedgeBounds(bounds, beginRadians, spanRadians);
			return [bounds];
		}

		/**
		 * This function returns a Bounds2D object set to the data bounds associated with the background.
		 * @param outputDataBounds A Bounds2D object to store the result in.
		 */
		override public function getBackgroundDataBounds():IBounds2D
		{
			return getReusableBounds(-1, -1, 1, 1);
		}
	}
}

/*
    Weave (Web-based Analysis and Visualization Environment)
    Copyright (C) 2008-2011 University of Massachusetts Lowell

    This file is a part of Weave.

    Weave is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License, Version 3,
    as published by the Free Software Foundation.

    Weave 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Weave.  If not, see <http://www.gnu.org/licenses/>.
*/

package weave.visualization.plotters
{
	import flash.display.BitmapData;
	import flash.display.Graphics;
	import flash.display.Shape;
	import flash.geom.Point;

	import mx.controls.Alert;

	import weave.Weave;
	import weave.api.data.IAttributeColumn;
	import weave.api.data.IQualifiedKey;
	import weave.api.linkSessionState;
	import weave.api.primitives.IBounds2D;
	import weave.data.AttributeColumns.DynamicColumn;
	import weave.data.AttributeColumns.EquationColumn;
	import weave.data.AttributeColumns.FilteredColumn;
	import weave.data.AttributeColumns.SortedColumn;
	import weave.primitives.Bounds2D;
	import weave.utils.BitmapText;
	import weave.utils.ColumnUtils;
	import weave.visualization.plotters.styles.DynamicFillStyle;
	import weave.visualization.plotters.styles.DynamicLineStyle;
	import weave.visualization.plotters.styles.SolidFillStyle;
	import weave.visualization.plotters.styles.SolidLineStyle;

	/**
	 * PieChartPlotter
	 *
	 * @author adufilie
	 */
	public class PieChartPlotter extends AbstractPlotter
	{
		public function PieChartPlotter()
		{
			init();
		}

		private var _beginRadians:EquationColumn;
		private var _spanRadians:EquationColumn;
		private var _filteredData:FilteredColumn;
		private var _labelColumn:DynamicColumn = new DynamicColumn();

		public function get data():DynamicColumn { return _filteredData.internalDynamicColumn; }
		public function get label():DynamicColumn {return _labelColumn;}

		public const lineStyle:DynamicLineStyle = new DynamicLineStyle(SolidLineStyle);
		public const fillStyle:DynamicFillStyle = new DynamicFillStyle(SolidFillStyle);

		private function init():void
		{
			var fill:SolidFillStyle = fillStyle.internalObject as SolidFillStyle;
			fill.color.internalDynamicColumn.globalName = Weave.DEFAULT_COLOR_COLUMN;

			_beginRadians = new EquationColumn();
			_beginRadians.equation.value = "0.5 * PI + getRunningTotal(spanRadians) - getNumber(spanRadians)";
			_spanRadians = _beginRadians.requestVariable("spanRadians", EquationColumn, true);
			_spanRadians.equation.value = "getNumber(sortedData) / getSum(sortedData) * 2 * PI";
			var sortedData:SortedColumn = _spanRadians.requestVariable("sortedData", SortedColumn, true);
			_filteredData = sortedData.internalDynamicColumn.requestLocalObject(FilteredColumn, true);
			linkSessionState(keySet.keyFilter, _filteredData.filter);

			registerSpatialProperties(data);
			registerNonSpatialProperties(fillStyle, lineStyle, label);
			setKeySource(_filteredData);
		}

		override protected function addRecordGraphicsToTempShape(recordKey:IQualifiedKey, dataBounds:IBounds2D, screenBounds:IBounds2D, tempShape:Shape):void
		{
			// project data coordinates to screen coordinates and draw graphics
			var beginRadians:Number = _beginRadians.getValueFromKey(recordKey, Number);
			var spanRadians:Number = _spanRadians.getValueFromKey(recordKey, Number);

			var graphics:Graphics = tempShape.graphics;
			// begin line & fill
			lineStyle.beginLineStyle(recordKey, graphics);
			fillStyle.beginFillStyle(recordKey, graphics);
			// move to center point
			WedgePlotter.drawProjectedWedge(graphics, dataBounds, screenBounds, beginRadians, spanRadians);
			// end fill
			graphics.endFill();
		}

		override public function drawBackground(dataBounds:IBounds2D, screenBounds:IBounds2D, destination:BitmapData):void
		{
			if (_labelColumn.keys.length == 0) return;

			var recordKey:IQualifiedKey;
			var beginRadians:Number;
			var spanRadians:Number;
			var midRadians:Number;
			var xScreenRadius:Number;
			var yScreenRadius:Number;
			var coordinate:Point = new Point();

			for (var i:int ; i < _filteredData.keys.length ; i++)
			{
				if (_labelColumn.containsKey(_filteredData.keys[i] as IQualifiedKey) == false) return;
				recordKey = _filteredData.keys[i] as IQualifiedKey;
				beginRadians = _beginRadians.getValueFromKey(recordKey, Number) as Number;
				spanRadians = _spanRadians.getValueFromKey(recordKey, Number) as Number;
				midRadians = beginRadians + (spanRadians / 2);

				coordinate.x = Math.cos(midRadians);
				coordinate.y = Math.sin(midRadians);
				dataBounds.projectPointTo(coordinate, screenBounds);
				coordinate.x += Math.cos(midRadians) * 10 * screenBounds.getXDirection();
				coordinate.y += Math.sin(midRadians) * 10 * screenBounds.getYDirection();

				var labelText:BitmapText = new BitmapText();
				labelText.text = ("  " + _labelColumn.getValueFromKey((_filteredData.keys[i] as IQualifiedKey)) + "  ");
				if (midRadians < (Math.PI / 2) || midRadians > ((3 * Math.PI) / 2))
				{
					labelText.horizontalAlign = BitmapText.HORIZONTAL_ALIGN_LEFT;
					labelText.verticalAlign = BitmapText.VERTICAL_ALIGN_CENTER;
				}
				else if (midRadians == ((3 * Math.PI) / 2))
				{
					labelText.horizontalAlign = BitmapText.HORIZONTAL_ALIGN_CENTER;
				}
				else if (midRadians == (Math.PI / 2))
				{
					labelText.horizontalAlign = BitmapText.HORIZONTAL_ALIGN_CENTER ;
					labelText.verticalAlign = BitmapText.VERTICAL_ALIGN_BOTTOM ;
				}
				else
				{
					labelText.horizontalAlign = BitmapText.HORIZONTAL_ALIGN_RIGHT;
					labelText.verticalAlign = BitmapText.VERTICAL_ALIGN_CENTER;
				}

				labelText.textFormat.color=Weave.properties.axisFontColor.value;
				labelText.textFormat.size=Weave.properties.axisFontSize.value;
				labelText.textFormat.underline=Weave.properties.axisFontUnderline.value;
				labelText.x = coordinate.x;
				labelText.y = coordinate.y;
				labelText.draw(destination);
			}
		}

		/**
		 * This gets the data bounds of the bin that a record key falls into.
		 */
		override public function getDataBoundsFromRecordKey(recordKey:IQualifiedKey):Array
		{
			var beginRadians:Number = _beginRadians.getValueFromKey(recordKey, Number);
			var spanRadians:Number = _spanRadians.getValueFromKey(recordKey, Number);
			var bounds:IBounds2D = getReusableBounds();
			WedgePlotter.getWedgeBounds(bounds, beginRadians, spanRadians);
			return [bounds];
		}

		/**
		 * This function returns a Bounds2D object set to the data bounds associated with the background.
		 * @param outputDataBounds A Bounds2D object to store the result in.
		 */
		override public function getBackgroundDataBounds():IBounds2D
		{
			return getReusableBounds(-1, -1, 1, 1);
		}
	}
}

Modify coordinate for pie chart label

Modify coordinate for pie chart label


package goplayer
{
  public class ConfigurationParser
  {
    public static const DEFAULT_STREAMIO_API_URL : String
      = "http://staging.streamio.com/api"
    public static const DEFAULT_SKIN_URL : String = "goplayer-skin.swf"
    public static const DEFAULT_TRACKER_ID : String = "global"

    public static const VALID_PARAMETERS : Array = [
      "streamio:api", "tracker", "skin", "video", "bitrate",
      "enablertmp", "autoplay", "loop",
      "skin:showchrome", "skin:showtitle" ]

    private const result : Configuration = new Configuration

    private var parameters : Object
    private var originalParameterNames : Object

    public function ConfigurationParser
      (parameters : Object, originalParameterNames : Object)
    {
      this.parameters = parameters
      this.originalParameterNames = originalParameterNames
    }

    public function execute() : void
    {
      result.apiURL = getString("streamio:api", DEFAULT_STREAMIO_API_URL)
      result.trackerID = getString("tracker", DEFAULT_TRACKER_ID)
      result.skinURL = getString("skin", DEFAULT_SKIN_URL)
      result.movieID = getString("video", null)
      result.bitratePolicy = getBitratePolicy("bitrate", BitratePolicy.BEST)
      result.enableRTMP = getBoolean("enablertmp", true)
      result.enableAutoplay = getBoolean("autoplay", false)
      result.enableLooping = getBoolean("loop", false)
      result.enableChrome = getBoolean("skin:showchrome", true)
      result.enableTitle = getBoolean("skin:showtitle", true)
    }

    public static function parse(parameters : Object) : Configuration
    {
      const normalizedParameters : Object = {}
      const originalParameterNames : Object = {}

      for (var name : String in parameters)
        if (VALID_PARAMETERS.indexOf(normalize(name)) == -1)
          reportUnknownParameter(name)
        else
          normalizedParameters[normalize(name)] = parameters[name],
            originalParameterNames[normalize(name)] = name

      const parser : ConfigurationParser = new ConfigurationParser
        (normalizedParameters, originalParameterNames)

      parser.execute()

      return parser.result
    }

    private static function normalize(name : String) : String
    { return name.toLowerCase().replace(/[^a-z:]/g, "") }

    private static function reportUnknownParameter(name : String) : void
    { debug("Error: Unknown parameter: " + name) }

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

    private function getString(name : String, fallback : String) : String
    { return name in parameters ? parameters[name] : fallback }

    private function getBoolean
      (name : String, fallback : Boolean) : Boolean
    {
      if (name in parameters)
        return $getBoolean(name, parameters[name], fallback)
      else
        return fallback
    }

    private function $getBoolean
      (name : String, value : String, fallback : Boolean) : Boolean
    {
      try
        { return $$getBoolean(value) }
      catch (error : Error)
        {
          reportInvalidParameter(name, value, ["true", "false"])

          return fallback
        }

      throw new Error
    }

    private function $$getBoolean(value : String) : Boolean
    {
      if (value == "true")
        return true
      else if (value == "false")
        return false
      else
        throw new Error
    }

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

    private function getBitratePolicy
      (name : String, fallback : BitratePolicy) : BitratePolicy
    {
      if (name in parameters)
        return $getBitratePolicy(name, parameters[name], fallback)
      else
        return fallback
    }

    private function $getBitratePolicy
      (name : String, value : String,
       fallback : BitratePolicy) : BitratePolicy
    {
      try
        { return $$getBitratePolicy(value) }
      catch (error : Error)
        {
          reportInvalidParameter(name, value, BITRATE_POLICY_VALUES)

          return fallback
        }

      throw new Error
    }

    private const BITRATE_POLICY_VALUES : Array =
      ["<number>kbps", "min", "max", "best"]

    private function $$getBitratePolicy(value : String) : BitratePolicy
    {
      if (value == "max")
        return BitratePolicy.MAX
      else if (value == "min")
        return BitratePolicy.MIN
      else if (value == "best")
        return BitratePolicy.BEST
      else if (Bitrate.parse(value))
        return BitratePolicy.specific(Bitrate.parse(value))
      else
        throw new Error
    }

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

    private function reportInvalidParameter
      (name : String, value : String, validValues : Array) : void
    {
      debug("Error: Invalid parameter: " +
            "“" + originalParameterNames[name] + "=" + value + "”; " +
            getInvalidParameterHint(validValues) + ".")
    }

    private function getInvalidParameterHint(values : Array) : String
    { return $getInvalidParameterHint(getQuotedValues(values)) }

    private function $getInvalidParameterHint(values : Array) : String
    {
      return "please use " +
        "either " + values.slice(0, -1).join(", ") + " " +
        "or " + values[values.length - 1]
    }

    private function getQuotedValues(values : Array) : Array
    {
      var result : Array = []

      for each (var value : String in values)
        result.push("“" + value + "”")

      return result
    }
  }
}

package goplayer
{
  public class ConfigurationParser
  {
    public static const DEFAULT_STREAMIO_API_URL : String
      = "http://staging.streamio.com/api"
    public static const DEFAULT_SKIN_URL : String = "goplayer-skin.swf"
    public static const DEFAULT_STREAMIO_TRACKER_ID : String = "global"

    public static const VALID_PARAMETERS : Array = [
      "streamio:api", "streamio:tracker", "skin", "video", "bitrate",
      "enablertmp", "autoplay", "loop",
      "skin:showchrome", "skin:showtitle" ]

    private const result : Configuration = new Configuration

    private var parameters : Object
    private var originalParameterNames : Object

    public function ConfigurationParser
      (parameters : Object, originalParameterNames : Object)
    {
      this.parameters = parameters
      this.originalParameterNames = originalParameterNames
    }

    public function execute() : void
    {
      result.apiURL = getString
        ("streamio:api", DEFAULT_STREAMIO_API_URL)
      result.trackerID = getString
        ("streamio:tracker", DEFAULT_STREAMIO_TRACKER_ID)
      result.skinURL = getString("skin", DEFAULT_SKIN_URL)
      result.movieID = getString("video", null)
      result.bitratePolicy = getBitratePolicy("bitrate", BitratePolicy.BEST)
      result.enableRTMP = getBoolean("enablertmp", true)
      result.enableAutoplay = getBoolean("autoplay", false)
      result.enableLooping = getBoolean("loop", false)
      result.enableChrome = getBoolean("skin:showchrome", true)
      result.enableTitle = getBoolean("skin:showtitle", true)
    }

    public static function parse(parameters : Object) : Configuration
    {
      const normalizedParameters : Object = {}
      const originalParameterNames : Object = {}

      for (var name : String in parameters)
        if (VALID_PARAMETERS.indexOf(normalize(name)) == -1)
          reportUnknownParameter(name)
        else
          normalizedParameters[normalize(name)] = parameters[name],
            originalParameterNames[normalize(name)] = name

      const parser : ConfigurationParser = new ConfigurationParser
        (normalizedParameters, originalParameterNames)

      parser.execute()

      return parser.result
    }

    private static function normalize(name : String) : String
    { return name.toLowerCase().replace(/[^a-z:]/g, "") }

    private static function reportUnknownParameter(name : String) : void
    { debug("Error: Unknown parameter: " + name) }

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

    private function getString(name : String, fallback : String) : String
    { return name in parameters ? parameters[name] : fallback }

    private function getBoolean
      (name : String, fallback : Boolean) : Boolean
    {
      if (name in parameters)
        return $getBoolean(name, parameters[name], fallback)
      else
        return fallback
    }

    private function $getBoolean
      (name : String, value : String, fallback : Boolean) : Boolean
    {
      try
        { return $$getBoolean(value) }
      catch (error : Error)
        {
          reportInvalidParameter(name, value, ["true", "false"])

          return fallback
        }

      throw new Error
    }

    private function $$getBoolean(value : String) : Boolean
    {
      if (value == "true")
        return true
      else if (value == "false")
        return false
      else
        throw new Error
    }

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

    private function getBitratePolicy
      (name : String, fallback : BitratePolicy) : BitratePolicy
    {
      if (name in parameters)
        return $getBitratePolicy(name, parameters[name], fallback)
      else
        return fallback
    }

    private function $getBitratePolicy
      (name : String, value : String,
       fallback : BitratePolicy) : BitratePolicy
    {
      try
        { return $$getBitratePolicy(value) }
      catch (error : Error)
        {
          reportInvalidParameter(name, value, BITRATE_POLICY_VALUES)

          return fallback
        }

      throw new Error
    }

    private const BITRATE_POLICY_VALUES : Array =
      ["<number>kbps", "min", "max", "best"]

    private function $$getBitratePolicy(value : String) : BitratePolicy
    {
      if (value == "max")
        return BitratePolicy.MAX
      else if (value == "min")
        return BitratePolicy.MIN
      else if (value == "best")
        return BitratePolicy.BEST
      else if (Bitrate.parse(value))
        return BitratePolicy.specific(Bitrate.parse(value))
      else
        throw new Error
    }

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

    private function reportInvalidParameter
      (name : String, value : String, validValues : Array) : void
    {
      debug("Error: Invalid parameter: " +
            "“" + originalParameterNames[name] + "=" + value + "”; " +
            getInvalidParameterHint(validValues) + ".")
    }

    private function getInvalidParameterHint(values : Array) : String
    { return $getInvalidParameterHint(getQuotedValues(values)) }

    private function $getInvalidParameterHint(values : Array) : String
    {
      return "please use " +
        "either " + values.slice(0, -1).join(", ") + " " +
        "or " + values[values.length - 1]
    }

    private function getQuotedValues(values : Array) : Array
    {
      var result : Array = []

      for each (var value : String in values)
        result.push("“" + value + "”")

      return result
    }
  }
}

Rename `tracker' parameter to `streamio:tracker'.


/* 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.hls {
    import org.mangui.hls.model.AudioTrack;

    import flash.display.Stage;
    import flash.net.NetConnection;
    import flash.net.NetStream;
    import flash.net.URLStream;
    import flash.events.EventDispatcher;
    import flash.events.Event;

    import org.mangui.hls.model.Level;
    import org.mangui.hls.event.HLSEvent;
    import org.mangui.hls.playlist.AltAudioTrack;
    import org.mangui.hls.loader.ManifestLoader;
    import org.mangui.hls.controller.AudioTrackController;
    import org.mangui.hls.loader.FragmentLoader;
    import org.mangui.hls.stream.HLSNetStream;
    import org.hola.WorkerUtils;
    import org.hola.HSettings;
    import flash.external.ExternalInterface;
    import org.hola.ZExternalInterface;

    CONFIG::LOGGING {
        import org.mangui.hls.utils.Log;
    }
    /** Class that manages the streaming process. **/
    public class HLS extends EventDispatcher {
        public var _fragmentLoader : FragmentLoader;
        private var _manifestLoader : ManifestLoader;
        private var _audioTrackController : AudioTrackController;
        /** HLS NetStream **/
        private var _hlsNetStream : HLSNetStream;
        /** HLS URLStream **/
        private var _hlsURLStream : Class;
        private var _client : Object = {};
        private var _stage : Stage;
        private var _url:String;
        private static var hola_api_inited:Boolean;
        private static var g_curr_id:Number = 0;
        private static var g_curr_hls:HLS;

        private static function hola_version() : Object
        {
            return {
                flashls_version: '0.3.5',
                patch_version: '1.0.12',
                have_worker: CONFIG::HAVE_WORKER
            };
        }
        private static function hola_hls_get_video_url() : String {
            return g_curr_hls._url;
        }

        private static function hola_hls_get_position() : Number {
            return g_curr_hls.position;
        }

        private static function hola_hls_get_duration() : Number {
            return g_curr_hls.duration;
        }

        private static function hola_hls_get_buffer_sec() : Number {
            return g_curr_hls.bufferLength;
        }

        private static function hola_hls_call(method:String, args:Array):Object{
            return g_curr_hls[method].apply(g_curr_hls, args);
        }

        private static function hola_hls_get_state() : String {
            return g_curr_hls.playbackState;
        }

        private static function hola_hls_get_levels() : Object {
            return g_curr_hls.levels
        }

        private static function hola_hls_get_level() : Number {
            return g_curr_hls.level
        }

        /** Create and connect all components. **/
        public function HLS() {
            HSettings.init();
            WorkerUtils.start_worker();
            if (!hola_api_inited && ZExternalInterface.avail())
            {
                ExternalInterface.call('console.log', 'HLS hola_api_inited');
                hola_api_inited = true;
                ExternalInterface.addCallback("hola_hls_call", HLS.hola_hls_call);
                ExternalInterface.addCallback("hola_version", HLS.hola_version);
                ExternalInterface.addCallback("hola_hls_get_video_url", HLS.hola_hls_get_video_url);
                ExternalInterface.addCallback("hola_hls_get_position", HLS.hola_hls_get_position);
                ExternalInterface.addCallback("hola_hls_get_duration", HLS.hola_hls_get_duration);
                ExternalInterface.addCallback("hola_hls_get_buffer_sec", HLS.hola_hls_get_buffer_sec);
                ExternalInterface.addCallback("hola_hls_get_state", HLS.hola_hls_get_state);
                ExternalInterface.addCallback("hola_hls_get_levels", HLS.hola_hls_get_levels);
                ExternalInterface.addCallback("hola_hls_get_level", HLS.hola_hls_get_level);
            }
            g_curr_id++;
            g_curr_hls = this;
            if (ZExternalInterface.avail())
            {
                ExternalInterface.call('console.log', 'HLS new ', g_curr_id);
                ExternalInterface.call('window.postMessage',
                    {id: 'flashls.hlsNew', hls_id: g_curr_id}, '*');
            }
            var connection : NetConnection = new NetConnection();
            connection.connect(null);
            _manifestLoader = new ManifestLoader(this);
            _audioTrackController = new AudioTrackController(this);
            _hlsURLStream = URLStream as Class;
            // default loader
            _fragmentLoader = new FragmentLoader(this, _audioTrackController);
            _hlsNetStream = new HLSNetStream(connection, this, _fragmentLoader);

            add_event(HLSEvent.MANIFEST_LOADING);
            add_event(HLSEvent.MANIFEST_PARSED);
            add_event(HLSEvent.MANIFEST_LOADED);
            add_event(HLSEvent.LEVEL_LOADING);
            add_event(HLSEvent.LEVEL_LOADED);
            add_event(HLSEvent.LEVEL_SWITCH);
            add_event(HLSEvent.LEVEL_ENDLIST);
            add_event(HLSEvent.FRAGMENT_LOADING);
            add_event(HLSEvent.FRAGMENT_LOADED);
            add_event(HLSEvent.FRAGMENT_PLAYING);
            add_event(HLSEvent.AUDIO_TRACKS_LIST_CHANGE);
            add_event(HLSEvent.AUDIO_TRACK_CHANGE);
            add_event(HLSEvent.TAGS_LOADED);
            add_event(HLSEvent.LAST_VOD_FRAGMENT_LOADED);
            add_event(HLSEvent.ERROR);
            add_event(HLSEvent.MEDIA_TIME);
            add_event(HLSEvent.PLAYBACK_STATE);
            add_event(HLSEvent.SEEK_STATE);
            add_event(HLSEvent.PLAYBACK_COMPLETE);
            add_event(HLSEvent.PLAYLIST_DURATION_UPDATED);
            add_event(HLSEvent.ID3_UPDATED);
        };

        private function add_event(name:String):void{
            this.addEventListener(name, event_handler_func('flashls.'+name));
        }
        private function event_handler_func(name:String):Function{
            return function(event:HLSEvent):void{
                if (!ZExternalInterface.avail())
                    return;
                ExternalInterface.call('window.postMessage',
                    {id: name, hls_id: g_curr_id, url: event.url,
                    level: event.level, duration: event.duration,
                    levels: event.levels, error: event.error,
                    loadMetrics: event.loadMetrics,
                    playMetrics: event.playMetrics, mediatime: event.mediatime,
                    state: event.state, audioTrack: event.audioTrack}, '*');
            }
        }

        /** Forward internal errors. **/
        override public function dispatchEvent(event : Event) : Boolean {
            if (event.type == HLSEvent.ERROR) {
                CONFIG::LOGGING {
                    Log.error((event as HLSEvent).error);
                }
                _hlsNetStream.close();
            }
            return super.dispatchEvent(event);
        };

        public function dispose() : void {
            if (ZExternalInterface.avail())
            {
                ExternalInterface.call('window.postMessage',
                    {id: 'flashls.hlsDispose', hls_id: g_curr_id}, '*');
            }
            _fragmentLoader.dispose();
            _manifestLoader.dispose();
            _audioTrackController.dispose();
            _hlsNetStream.dispose_();
            _fragmentLoader = null;
            _manifestLoader = null;
            _audioTrackController = null;
            _hlsNetStream = null;
            _client = null;
            _stage = null;
            _hlsNetStream = null;
        }

        /** Return the quality level used when starting a fresh playback **/
        public function get startlevel() : int {
            return _manifestLoader.startlevel;
        };

        /** Return the quality level used after a seek operation **/
        public function get seeklevel() : int {
            return _manifestLoader.seeklevel;
        };

        /** Return the quality level of the currently played fragment **/
        public function get playbacklevel() : int {
            return _hlsNetStream.playbackLevel;
        };

        /** Return the quality level of last loaded fragment **/
        public function get level() : int {
            return _fragmentLoader.level;
        };

        /*  set quality level for next loaded fragment (-1 for automatic level selection) */
        public function set level(level : int) : void {
            _fragmentLoader.level = level;
        };

        /* check if we are in automatic level selection mode */
        public function get autolevel() : Boolean {
            return _fragmentLoader.autolevel;
        };

        /** Return a Vector of quality level **/
        public function get levels() : Vector.<Level> {
            return _manifestLoader.levels;
        };

        /** Return the current playback position. **/
        public function get position() : Number {
            return _hlsNetStream.position;
        };

        public function get video_url() : String {
            return _url;
        };

        public function get duration() : Number {
            return _hlsNetStream.duration;
        };

        /** Return the current playback state. **/
        public function get playbackState() : String {
            return _hlsNetStream.playbackState;
        };

        /** Return the current seek state. **/
        public function get seekState() : String {
            return _hlsNetStream.seekState;
        };

        /** Return the type of stream (VOD/LIVE). **/
        public function get type() : String {
            return _manifestLoader.type;
        };

        /** Load and parse a new HLS URL **/
        public function load(url : String) : void {
            _hlsNetStream.close();
            _url = url;
            _manifestLoader.load(url);
        };

        /** return HLS NetStream **/
        public function get stream() : NetStream {
            return _hlsNetStream;
        }

        public function get client() : Object {
            return _client;
        }

        public function set client(value : Object) : void {
            _client = value;
        }

        /** get current Buffer Length  **/
        public function get bufferLength() : Number {
            return _hlsNetStream.bufferLength;
        };

        /** get audio tracks list**/
        public function get audioTracks() : Vector.<AudioTrack> {
            return _audioTrackController.audioTracks;
        };

        /** get alternate audio tracks list from playlist **/
        public function get altAudioTracks() : Vector.<AltAudioTrack> {
            return _manifestLoader.altAudioTracks;
        };

        /** get index of the selected audio track (index in audio track lists) **/
        public function get audioTrack() : int {
            return _audioTrackController.audioTrack;
        };

        /** select an audio track, based on its index in audio track lists**/
        public function set audioTrack(val : int) : void {
            _audioTrackController.audioTrack = val;
        }

        /* set stage */
        public function set stage(stage : Stage) : void {
            _stage = stage;
        }

        /* get stage */
        public function get stage() : Stage {
            return _stage;
        }

        /* set URL stream loader */
        public function set URLstream(urlstream : Class) : void {
            _hlsURLStream = urlstream;
        }

        /* retrieve URL stream loader */
        public function get URLstream() : Class {
            return _hlsURLStream;
        }
    };
}

/* 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.hls {
    import org.mangui.hls.model.AudioTrack;

    import flash.display.Stage;
    import flash.net.NetConnection;
    import flash.net.NetStream;
    import flash.net.URLStream;
    import flash.events.EventDispatcher;
    import flash.events.Event;

    import org.mangui.hls.model.Level;
    import org.mangui.hls.event.HLSEvent;
    import org.mangui.hls.playlist.AltAudioTrack;
    import org.mangui.hls.loader.ManifestLoader;
    import org.mangui.hls.controller.AudioTrackController;
    import org.mangui.hls.loader.FragmentLoader;
    import org.mangui.hls.stream.HLSNetStream;
    import org.hola.WorkerUtils;
    import org.hola.HSettings;
    import org.hola.FlashFetchBin;
    import flash.external.ExternalInterface;
    import org.hola.ZExternalInterface;

    CONFIG::LOGGING {
        import org.mangui.hls.utils.Log;
    }
    /** Class that manages the streaming process. **/
    public class HLS extends EventDispatcher {
        public var _fragmentLoader : FragmentLoader;
        private var _manifestLoader : ManifestLoader;
        private var _audioTrackController : AudioTrackController;
        /** HLS NetStream **/
        private var _hlsNetStream : HLSNetStream;
        /** HLS URLStream **/
        private var _hlsURLStream : Class;
        private var _client : Object = {};
        private var _stage : Stage;
        private var _url:String;
        private static var hola_api_inited:Boolean;
        private static var g_curr_id:Number = 0;
        private static var g_curr_hls:HLS;

        private static function hola_version() : Object
        {
            return {
                flashls_version: '0.3.5',
                patch_version: '1.0.12',
                have_worker: CONFIG::HAVE_WORKER
            };
        }
        private static function hola_hls_get_video_url() : String {
            return g_curr_hls._url;
        }

        private static function hola_hls_get_position() : Number {
            return g_curr_hls.position;
        }

        private static function hola_hls_get_duration() : Number {
            return g_curr_hls.duration;
        }

        private static function hola_hls_get_buffer_sec() : Number {
            return g_curr_hls.bufferLength;
        }

        private static function hola_hls_call(method:String, args:Array):Object{
            return g_curr_hls[method].apply(g_curr_hls, args);
        }

        private static function hola_hls_get_state() : String {
            return g_curr_hls.playbackState;
        }

        private static function hola_hls_get_levels() : Object {
            return g_curr_hls.levels
        }

        private static function hola_hls_get_level() : Number {
            return g_curr_hls.level
        }

        /** Create and connect all components. **/
        public function HLS() {
            HSettings.init();
            WorkerUtils.start_worker();
            if (!hola_api_inited && ZExternalInterface.avail())
            {
                ExternalInterface.call('console.log', 'HLS hola_api_inited');
                hola_api_inited = true;
                ExternalInterface.addCallback("hola_hls_call", HLS.hola_hls_call);
                ExternalInterface.addCallback("hola_version", HLS.hola_version);
                ExternalInterface.addCallback("hola_hls_get_video_url", HLS.hola_hls_get_video_url);
                ExternalInterface.addCallback("hola_hls_get_position", HLS.hola_hls_get_position);
                ExternalInterface.addCallback("hola_hls_get_duration", HLS.hola_hls_get_duration);
                ExternalInterface.addCallback("hola_hls_get_buffer_sec", HLS.hola_hls_get_buffer_sec);
                ExternalInterface.addCallback("hola_hls_get_state", HLS.hola_hls_get_state);
                ExternalInterface.addCallback("hola_hls_get_levels", HLS.hola_hls_get_levels);
                ExternalInterface.addCallback("hola_hls_get_level", HLS.hola_hls_get_level);
                FlashFetchBin.init();
            }
            g_curr_id++;
            g_curr_hls = this;
            if (ZExternalInterface.avail())
            {
                ExternalInterface.call('console.log', 'HLS new ', g_curr_id);
                ExternalInterface.call('window.postMessage',
                    {id: 'flashls.hlsNew', hls_id: g_curr_id}, '*');
            }
            var connection : NetConnection = new NetConnection();
            connection.connect(null);
            _manifestLoader = new ManifestLoader(this);
            _audioTrackController = new AudioTrackController(this);
            _hlsURLStream = URLStream as Class;
            // default loader
            _fragmentLoader = new FragmentLoader(this, _audioTrackController);
            _hlsNetStream = new HLSNetStream(connection, this, _fragmentLoader);

            add_event(HLSEvent.MANIFEST_LOADING);
            add_event(HLSEvent.MANIFEST_PARSED);
            add_event(HLSEvent.MANIFEST_LOADED);
            add_event(HLSEvent.LEVEL_LOADING);
            add_event(HLSEvent.LEVEL_LOADED);
            add_event(HLSEvent.LEVEL_SWITCH);
            add_event(HLSEvent.LEVEL_ENDLIST);
            add_event(HLSEvent.FRAGMENT_LOADING);
            add_event(HLSEvent.FRAGMENT_LOADED);
            add_event(HLSEvent.FRAGMENT_PLAYING);
            add_event(HLSEvent.AUDIO_TRACKS_LIST_CHANGE);
            add_event(HLSEvent.AUDIO_TRACK_CHANGE);
            add_event(HLSEvent.TAGS_LOADED);
            add_event(HLSEvent.LAST_VOD_FRAGMENT_LOADED);
            add_event(HLSEvent.ERROR);
            add_event(HLSEvent.MEDIA_TIME);
            add_event(HLSEvent.PLAYBACK_STATE);
            add_event(HLSEvent.SEEK_STATE);
            add_event(HLSEvent.PLAYBACK_COMPLETE);
            add_event(HLSEvent.PLAYLIST_DURATION_UPDATED);
            add_event(HLSEvent.ID3_UPDATED);
        };

        private function add_event(name:String):void{
            this.addEventListener(name, event_handler_func('flashls.'+name));
        }
        private function event_handler_func(name:String):Function{
            return function(event:HLSEvent):void{
                if (!ZExternalInterface.avail())
                    return;
                ExternalInterface.call('window.postMessage',
                    {id: name, hls_id: g_curr_id, url: event.url,
                    level: event.level, duration: event.duration,
                    levels: event.levels, error: event.error,
                    loadMetrics: event.loadMetrics,
                    playMetrics: event.playMetrics, mediatime: event.mediatime,
                    state: event.state, audioTrack: event.audioTrack}, '*');
            }
        }

        /** Forward internal errors. **/
        override public function dispatchEvent(event : Event) : Boolean {
            if (event.type == HLSEvent.ERROR) {
                CONFIG::LOGGING {
                    Log.error((event as HLSEvent).error);
                }
                _hlsNetStream.close();
            }
            return super.dispatchEvent(event);
        };

        public function dispose() : void {
            if (ZExternalInterface.avail())
            {
                ExternalInterface.call('window.postMessage',
                    {id: 'flashls.hlsDispose', hls_id: g_curr_id}, '*');
            }
            _fragmentLoader.dispose();
            _manifestLoader.dispose();
            _audioTrackController.dispose();
            _hlsNetStream.dispose_();
            _fragmentLoader = null;
            _manifestLoader = null;
            _audioTrackController = null;
            _hlsNetStream = null;
            _client = null;
            _stage = null;
            _hlsNetStream = null;
        }

        /** Return the quality level used when starting a fresh playback **/
        public function get startlevel() : int {
            return _manifestLoader.startlevel;
        };

        /** Return the quality level used after a seek operation **/
        public function get seeklevel() : int {
            return _manifestLoader.seeklevel;
        };

        /** Return the quality level of the currently played fragment **/
        public function get playbacklevel() : int {
            return _hlsNetStream.playbackLevel;
        };

        /** Return the quality level of last loaded fragment **/
        public function get level() : int {
            return _fragmentLoader.level;
        };

        /*  set quality level for next loaded fragment (-1 for automatic level selection) */
        public function set level(level : int) : void {
            _fragmentLoader.level = level;
        };

        /* check if we are in automatic level selection mode */
        public function get autolevel() : Boolean {
            return _fragmentLoader.autolevel;
        };

        /** Return a Vector of quality level **/
        public function get levels() : Vector.<Level> {
            return _manifestLoader.levels;
        };

        /** Return the current playback position. **/
        public function get position() : Number {
            return _hlsNetStream.position;
        };

        public function get video_url() : String {
            return _url;
        };

        public function get duration() : Number {
            return _hlsNetStream.duration;
        };

        /** Return the current playback state. **/
        public function get playbackState() : String {
            return _hlsNetStream.playbackState;
        };

        /** Return the current seek state. **/
        public function get seekState() : String {
            return _hlsNetStream.seekState;
        };

        /** Return the type of stream (VOD/LIVE). **/
        public function get type() : String {
            return _manifestLoader.type;
        };

        /** Load and parse a new HLS URL **/
        public function load(url : String) : void {
            _hlsNetStream.close();
            _url = url;
            _manifestLoader.load(url);
        };

        /** return HLS NetStream **/
        public function get stream() : NetStream {
            return _hlsNetStream;
        }

        public function get client() : Object {
            return _client;
        }

        public function set client(value : Object) : void {
            _client = value;
        }

        /** get current Buffer Length  **/
        public function get bufferLength() : Number {
            return _hlsNetStream.bufferLength;
        };

        /** get audio tracks list**/
        public function get audioTracks() : Vector.<AudioTrack> {
            return _audioTrackController.audioTracks;
        };

        /** get alternate audio tracks list from playlist **/
        public function get altAudioTracks() : Vector.<AltAudioTrack> {
            return _manifestLoader.altAudioTracks;
        };

        /** get index of the selected audio track (index in audio track lists) **/
        public function get audioTrack() : int {
            return _audioTrackController.audioTrack;
        };

        /** select an audio track, based on its index in audio track lists**/
        public function set audioTrack(val : int) : void {
            _audioTrackController.audioTrack = val;
        }

        /* set stage */
        public function set stage(stage : Stage) : void {
            _stage = stage;
        }

        /* get stage */
        public function get stage() : Stage {
            return _stage;
        }

        /* set URL stream loader */
        public function set URLstream(urlstream : Class) : void {
            _hlsURLStream = urlstream;
        }

        /* retrieve URL stream loader */
        public function get URLstream() : Class {
            return _hlsURLStream;
        }
    };
}

fix calling FlashFetchBin.init

fix calling FlashFetchBin.init


package dolly {

import dolly.core.dolly_internal;
import dolly.core.errors.CloningError;
import dolly.core.metadata.MetadataName;
import dolly.utils.PropertyUtil;

import org.as3commons.reflect.Accessor;
import org.as3commons.reflect.AccessorAccess;
import org.as3commons.reflect.Field;
import org.as3commons.reflect.Type;

use namespace dolly_internal;

public class Cloner {

	private static function isTypeCloneable(type:Type):Boolean {
		return type.hasMetadata(MetadataName.CLONEABLE);
	}

	private static function failIfTypeIsNotCloneable(type:Type):void {
		if (!isTypeCloneable(type)) {
			throw new CloningError(
					CloningError.CLASS_IS_NOT_CLONEABLE_MESSAGE,
					CloningError.CLASS_IS_NOT_CLONEABLE_CODE
			);
		}
	}

	dolly_internal static function findAllWritableFieldsForType(type:Type):Vector.<Field> {
		failIfTypeIsNotCloneable(type);

		const result:Vector.<Field> = new Vector.<Field>();

		for each(var field:Field in type.properties) {
			if (!(field is Accessor) || (field as Accessor).access == AccessorAccess.READ_WRITE) {
				result.push(field);
			}
		}

		return result;
	}

	dolly_internal static function doClone(source:*, deep:int, type:Type = null):* {
		if(!type) {
			type = Type.forInstance(source);
		}

		const fieldsToClone:Vector.<Field> = findAllWritableFieldsForType(type);
		const clonedInstance:* = new (type.clazz)();

		for each(var field:Field in fieldsToClone) {
			PropertyUtil.cloneProperty(source, clonedInstance, field.name);
		}

		return clonedInstance;
	}

	public static function clone(source:*):* {
		const type:Type = Type.forInstance(source);

		failIfTypeIsNotCloneable(type);

		const deep:int = 2;

		return doClone(source, deep, type);
	}
}
}

package dolly {

import dolly.core.dolly_internal;
import dolly.core.errors.CloningError;
import dolly.core.metadata.MetadataName;
import dolly.utils.PropertyUtil;

import flash.utils.Dictionary;

import org.as3commons.reflect.Accessor;
import org.as3commons.reflect.AccessorAccess;
import org.as3commons.reflect.Field;
import org.as3commons.reflect.Type;

use namespace dolly_internal;

public class Cloner {

	private static function isTypeCloneable(type:Type):Boolean {
		return type.hasMetadata(MetadataName.CLONEABLE);
	}

	private static function failIfTypeIsNotCloneable(type:Type):void {
		if (!isTypeCloneable(type)) {
			throw new CloningError(
					CloningError.CLASS_IS_NOT_CLONEABLE_MESSAGE,
					CloningError.CLASS_IS_NOT_CLONEABLE_CODE
			);
		}
	}

	dolly_internal static function findAllWritableFieldsForType(type:Type):Vector.<Field> {
		failIfTypeIsNotCloneable(type);

		const result:Vector.<Field> = new Vector.<Field>();

		for each(var field:Field in type.properties) {
			if (!(field is Accessor) || (field as Accessor).access == AccessorAccess.READ_WRITE) {
				result.push(field);
			}
		}

		return result;
	}

	dolly_internal static function doClone(source:*, deep:int, clonedObjectsMap:Dictionary = null, type:Type = null):* {
		if (!clonedObjectsMap) {
			clonedObjectsMap = new Dictionary();
		}

		if (!type) {
			type = Type.forInstance(source);
		}

		const fieldsToClone:Vector.<Field> = findAllWritableFieldsForType(type);
		const clonedInstance:* = new (type.clazz)();

		for each(var field:Field in fieldsToClone) {
			PropertyUtil.cloneProperty(source, clonedInstance, field.name);
		}

		return clonedInstance;
	}

	public static function clone(source:*):* {
		const type:Type = Type.forInstance(source);

		failIfTypeIsNotCloneable(type);

		const deep:int = 2;

		return doClone(source, deep, null, type);
	}
}
}

Add parameter "clonedObjects" in Cloner.doClone() method.


package dolly {

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

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

use namespace dolly_internal;

public class Copier {

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

	dolly_internal static function findCopyableFieldsForType(type:Type):Vector.<Field> {
		const result:Vector.<Field> = new Vector.<Field>();

		var variable:Variable;
		var accessor:Accessor;

		const isTypeCloneable:Boolean = type.hasMetadata(MetadataName.COPYABLE);
		if (isTypeCloneable) {
			for each(variable in type.variables) {
				if (!variable.isStatic) {
					result.push(variable);
				}
			}
			for each(accessor in type.accessors) {
				if (isAccessorCopyable(accessor)) {
					result.push(accessor);
				}
			}
		} else {
			const metadataContainers:Array = type.getMetadataContainers(MetadataName.COPYABLE);
			for each(var metadataContainer:IMetadataContainer in metadataContainers) {
				if (metadataContainer is Variable) {
					variable = metadataContainer as Variable;
					if (!variable.isStatic && variable.hasMetadata(MetadataName.COPYABLE)) {
						result.push(variable);
					}
				} else if (metadataContainer is Accessor) {
					accessor = metadataContainer as Accessor;
					if (isAccessorCopyable(accessor, false)) {
						result.push(accessor);
					}
				}
			}
		}

		return result;
	}

	public static function copy(source:*):* {
		const type:Type = Type.forInstance(source);
		const copy:* = new (type.clazz)();

		const fieldsToCopy:Vector.<Field> = findCopyableFieldsForType(type);
		var name:String;
		for each(var field:Field in fieldsToCopy) {
			name = field.name;
			copy[name] = source[name];
		}

		return copy;
	}
}
}

package dolly {

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

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

use namespace dolly_internal;

public class Copier {

	dolly_internal static function findCopyableFieldsForType(type:Type):Vector.<Field> {
		const result:Vector.<Field> = new Vector.<Field>();

		var variable:Variable;
		var accessor:Accessor;

		const isTypeCloneable:Boolean = type.hasMetadata(MetadataName.COPYABLE);
		if (isTypeCloneable) {
			for each(variable in type.variables) {
				if (!variable.isStatic) {
					result.push(variable);
				}
			}
			for each(accessor in type.accessors) {
				if (!accessor.isStatic && accessor.access == AccessorAccess.READ_WRITE) {
					result.push(accessor);
				}
			}
		} else {
			const metadataContainers:Array = type.getMetadataContainers(MetadataName.COPYABLE);
			for each(var metadataContainer:IMetadataContainer in metadataContainers) {
				if (metadataContainer is Variable) {
					variable = metadataContainer as Variable;
					if (!variable.isStatic && variable.hasMetadata(MetadataName.COPYABLE)) {
						result.push(variable);
					}
				} else if (metadataContainer is Accessor) {
					accessor = metadataContainer as Accessor;
					if (!accessor.isStatic
							&& accessor.access == AccessorAccess.READ_WRITE
							&& accessor.hasMetadata(MetadataName.COPYABLE)) {
						result.push(accessor);
					}
				}
			}
		}

		return result;
	}

	public static function copy(source:*):* {
		const type:Type = Type.forInstance(source);
		const copy:* = new (type.clazz)();

		const fieldsToCopy:Vector.<Field> = findCopyableFieldsForType(type);
		var name:String;
		for each(var field:Field in fieldsToCopy) {
			name = field.name;
			copy[name] = source[name];
		}

		return copy;
	}
}
}

Remove private static method Copier.isAccessorCopyable().


package aerys.minko.scene.node
{
	import aerys.minko.ns.minko_scene;
	import aerys.minko.render.Effect;
	import aerys.minko.render.Viewport;
	import aerys.minko.scene.controller.scene.RenderingController;
	import aerys.minko.scene.node.camera.AbstractCamera;
	import aerys.minko.type.Signal;
	import aerys.minko.type.binding.DataBindings;
	import aerys.minko.type.binding.DataProvider;
	import aerys.minko.type.enum.DataProviderUsage;
	import aerys.minko.type.loader.AssetsLibrary;

	import flash.display.BitmapData;
	import flash.utils.Dictionary;
	import flash.utils.getTimer;

	/**
	 * Scene objects are the root of any 3D scene.
	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public final class Scene extends Group
	{
		use namespace minko_scene;

		minko_scene var _camera		: AbstractCamera;

		private var _renderingCtrl	: RenderingController;

		private var _properties		: DataProvider;
		private var _bindings		: DataBindings;

		private var _numTriangles	: uint;

		private var _enterFrame			: Signal;
		private var _renderingBegin		: Signal;
		private var _renderingEnd		: Signal;
		private var _exitFrame			: Signal;

		private var _assets				: AssetsLibrary;

		public function get assets():AssetsLibrary
		{
			return _assets;
		}

		public function get activeCamera() : AbstractCamera
		{
			return _camera;
		}

		public function get numPasses() : uint
		{
			return _renderingCtrl.numPasses;
		}

		public function get numEnabledPasses() : uint
		{
			return _renderingCtrl.numEnabledPasses;
		}

		public function get numTriangles() : uint
		{
			return _numTriangles;
		}

		public function get postProcessingEffect() : Effect
		{
			return _renderingCtrl.postProcessingEffect;
		}
		public function set postProcessingEffect(value : Effect) : void
		{
			_renderingCtrl.postProcessingEffect = value;
		}

		public function get postProcessingProperties() : DataProvider
		{
			return _renderingCtrl.postProcessingProperties;
		}

		public function get properties() : DataProvider
		{
			return _properties;
		}
		public function set properties(value : DataProvider) : void
		{
			if (_properties != value)
			{
				if (_properties)
					_bindings.removeProvider(_properties);

				_properties = value;

				if (value)
					_bindings.addProvider(value);
			}
		}

		public function get bindings() : DataBindings
		{
			return _bindings;
		}

		/**
		 * The signal executed when the viewport is about to start rendering a frame.
		 * Callback functions for this signal should accept the following arguments:
		 *

		 *
viewport : Viewport, the viewport who starts rendering the frame

		 *

		 * @return
		 *
		 */
		public function get enterFrame() : Signal
		{
			return _enterFrame;
		}

		/**
		 * The signal executed when the viewport is done rendering a frame.
		 * Callback functions for this signal should accept the following arguments:
		 *

		 *
viewport : Viewport, the viewport who just finished rendering the frame

		 *

		 * @return
		 *
		 */
		public function get exitFrame() : Signal
		{
			return _exitFrame;
		}

		public function get renderingBegin() : Signal
		{
			return _renderingBegin;
		}

		public function get renderingEnd() : Signal
		{
			return _renderingEnd;
		}

		public function Scene(...children)
		{
			super(children);
		}

		override protected function initialize() : void
		{
			_bindings 					= new DataBindings(this);
			_assets						= new AssetsLibrary();
			this.properties 			= new DataProvider(DataProviderUsage.EXCLUSIVE);

			super.initialize();
		}

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

			_enterFrame			= new Signal('Scene.enterFrame');
			_renderingBegin 	= new Signal('Scene.renderingBegin');
			_renderingEnd 		= new Signal('Scene.renderingEnd');
			_exitFrame 			= new Signal('Scene.exitFrame');
		}

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

			added.add(addedHandler);
		}

		override protected function initializeContollers() : void
		{
			_renderingCtrl = new RenderingController();
			addController(_renderingCtrl);

			super.initializeContollers();
		}

		public function render(viewport : Viewport, destination : BitmapData = null) : void
		{
			_enterFrame.execute(this, viewport, destination, getTimer());
			if (viewport.ready && viewport.visible)
			{
				_renderingBegin.execute(this, viewport, destination, getTimer());
				_numTriangles = _renderingCtrl.render(viewport, destination);
				_renderingEnd.execute(this, viewport, destination, getTimer());
			}
			_exitFrame.execute(this, viewport, destination, getTimer());
		}

		private function addedHandler(child : ISceneNode, parent : Group) : void
		{
			throw new Error();
		}
	}
}

package aerys.minko.scene.node
{
	import aerys.minko.ns.minko_scene;
	import aerys.minko.render.Effect;
	import aerys.minko.render.Viewport;
	import aerys.minko.scene.controller.scene.RenderingController;
	import aerys.minko.scene.node.camera.AbstractCamera;
	import aerys.minko.type.Signal;
	import aerys.minko.type.binding.DataBindings;
	import aerys.minko.type.binding.DataProvider;
	import aerys.minko.type.enum.DataProviderUsage;
	import aerys.minko.type.loader.AssetsLibrary;

	import flash.display.BitmapData;
	import flash.utils.Dictionary;
	import flash.utils.getTimer;

	/**
	 * Scene objects are the root of any 3D scene.
	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public final class Scene extends Group
	{
		use namespace minko_scene;

		minko_scene var _camera		: AbstractCamera;

		private var _renderingCtrl	: RenderingController;

		private var _properties		: DataProvider;
		private var _bindings		: DataBindings;

		private var _numTriangles	: uint;

		private var _enterFrame		: Signal;
		private var _renderingBegin	: Signal;
		private var _renderingEnd	: Signal;
		private var _exitFrame		: Signal;

		private var _assets				: AssetsLibrary;

		public function get assets() : AssetsLibrary
		{
			return _assets;
		}

		public function get activeCamera() : AbstractCamera
		{
			return _camera;
		}

		public function get numPasses() : uint
		{
			return _renderingCtrl.numPasses;
		}

		public function get numEnabledPasses() : uint
		{
			return _renderingCtrl.numEnabledPasses;
		}

		public function get numTriangles() : uint
		{
			return _numTriangles;
		}

		public function get postProcessingEffect() : Effect
		{
			return _renderingCtrl.postProcessingEffect;
		}
		public function set postProcessingEffect(value : Effect) : void
		{
			_renderingCtrl.postProcessingEffect = value;
		}

		public function get postProcessingProperties() : DataProvider
		{
			return _renderingCtrl.postProcessingProperties;
		}

		public function get properties() : DataProvider
		{
			return _properties;
		}
		public function set properties(value : DataProvider) : void
		{
			if (_properties != value)
			{
				if (_properties)
					_bindings.removeProvider(_properties);

				_properties = value;

				if (value)
					_bindings.addProvider(value);
			}
		}

		public function get bindings() : DataBindings
		{
			return _bindings;
		}

		/**
		 * The signal executed when the viewport is about to start rendering a frame.
		 * Callback functions for this signal should accept the following arguments:
		 *

		 *
viewport : Viewport, the viewport who starts rendering the frame

		 *

		 * @return
		 *
		 */
		public function get enterFrame() : Signal
		{
			return _enterFrame;
		}

		/**
		 * The signal executed when the viewport is done rendering a frame.
		 * Callback functions for this signal should accept the following arguments:
		 *

		 *
viewport : Viewport, the viewport who just finished rendering the frame

		 *

		 * @return
		 *
		 */
		public function get exitFrame() : Signal
		{
			return _exitFrame;
		}

		public function get renderingBegin() : Signal
		{
			return _renderingBegin;
		}

		public function get renderingEnd() : Signal
		{
			return _renderingEnd;
		}

		public function Scene(...children)
		{
			super(children);
		}

		override protected function initialize() : void
		{
			_bindings 					= new DataBindings(this);
			_assets						= new AssetsLibrary();
			this.properties 			= new DataProvider(DataProviderUsage.EXCLUSIVE);

			super.initialize();
		}

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

			_enterFrame			= new Signal('Scene.enterFrame');
			_renderingBegin 	= new Signal('Scene.renderingBegin');
			_renderingEnd 		= new Signal('Scene.renderingEnd');
			_exitFrame 			= new Signal('Scene.exitFrame');
		}

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

			added.add(addedHandler);
		}

		override protected function initializeContollers() : void
		{
			_renderingCtrl = new RenderingController();
			addController(_renderingCtrl);

			super.initializeContollers();
		}

		public function render(viewport : Viewport, destination : BitmapData = null) : void
		{
			_enterFrame.execute(this, viewport, destination, getTimer());
			if (viewport.ready && viewport.visible)
			{
				_renderingBegin.execute(this, viewport, destination, getTimer());
				_numTriangles = _renderingCtrl.render(viewport, destination);
				_renderingEnd.execute(this, viewport, destination, getTimer());
			}
			_exitFrame.execute(this, viewport, destination, getTimer());
		}

		private function addedHandler(child : ISceneNode, parent : Group) : void
		{
			throw new Error();
		}
	}
}

fix coding style

fix coding style


package aerys.minko.render.shader.part.phong.attenuation
{
	import aerys.minko.render.material.phong.PhongProperties;
	import aerys.minko.render.shader.SFloat;
	import aerys.minko.render.shader.Shader;
	import aerys.minko.render.shader.part.phong.LightAwareShaderPart;
	import aerys.minko.type.enum.SamplerFiltering;
	import aerys.minko.type.enum.SamplerMipMapping;
	import aerys.minko.type.enum.SamplerWrapping;
	import aerys.minko.type.enum.ShadowMappingQuality;

	/**
	 * Fixme, bias should be:Total bias is m*SLOPESCALE + DEPTHBIAS
	 * Where m = max( | ∂z/∂x | , | ∂z/∂y | )
	 * ftp://download.nvidia.com/developer/presentations/2004/GPU_Jackpot/Shadow_Mapping.pdf
	 *
	 * @author Romain Gilliotte
	 */
	public class MatrixShadowMapAttenuationShaderPart extends LightAwareShaderPart implements IAttenuationShaderPart
	{
		public function MatrixShadowMapAttenuationShaderPart(main : Shader)
		{
			super(main);
		}

		public function getAttenuation(lightId : uint) : SFloat
		{
			var lightType	: uint		= getLightConstant(lightId, 'type');
			var screenPos	: SFloat	= interpolate(localToScreen(fsLocalPosition));

			// retrieve shadow bias
			var shadowBias : SFloat;
			if (meshBindings.propertyExists(PhongProperties.SHADOW_BIAS))
				shadowBias = meshBindings.getParameter(PhongProperties.SHADOW_BIAS, 1);
			else
				shadowBias = getLightParameter(lightId, PhongProperties.SHADOW_BIAS, 1);

			// retrieve depthmap and projection matrix
			var worldToUV	: SFloat = getLightParameter(lightId, 'worldToUV', 16);
			var depthMap	: SFloat = getLightTextureParameter(
				lightId,
				'shadowMap',
				SamplerFiltering.LINEAR,
				SamplerMipMapping.DISABLE,
				SamplerWrapping.CLAMP
			);

			// read expected depth from shadow map, and compute current depth
			var uv : SFloat;
			uv = multiply4x4(vsWorldPosition, worldToUV);
			uv = interpolate(uv);

			var currentDepth : SFloat = uv.z;
			currentDepth = min(subtract(1, shadowBias), currentDepth);

			uv = divide(uv, uv.w);

			var outsideMap			: SFloat = notEqual(
                0,
                dotProduct4(notEqual(uv, saturate(uv)), notEqual(uv, saturate(uv)))
            );

			var precomputedDepth	: SFloat = unpack(sampleTexture(depthMap, uv.xyyy));

			var curDepthSubBias		: SFloat = subtract(currentDepth, shadowBias);
			var noShadows			: SFloat = lessEqual(curDepthSubBias, precomputedDepth);

			var quality		: uint		= getLightConstant(lightId, 'shadowQuality');

			if (quality != ShadowMappingQuality.HARD)
			{
				var invertSize	: SFloat			= divide(
					getLightParameter(lightId, 'shadowSpread', 1),
					getLightParameter(lightId, 'shadowMapSize', 1)
				);

				var uvs 		: Vector.<SFloat>	= new <SFloat>[];
				var uvDelta		: SFloat;

				if (quality > ShadowMappingQuality.LOW)
				{
					uvDelta = multiply(float3(-1, 0, 1), invertSize);
					uvs.push(
						add(uv.xyxy, uvDelta.xxxy),	// (-1, -1), (-1,  0)
						add(uv.xyxy, uvDelta.xzyx),	// (-1,  1), ( 0, -1)
						add(uv.xyxy, uvDelta.yzzx),	// ( 0,  1), ( 1, -1)
						add(uv.xyxy, uvDelta.zyzz)	// ( 1,  0), ( 1,  1)
					);
				}

				if (quality > ShadowMappingQuality.MEDIUM)
				{
					uvDelta = multiply(float3(-2, 0, 2), invertSize);
					uvs.push(
						add(uv.xyxy, uvDelta.xyyx),	// (-2, 0), (0, -2)
						add(uv.xyxy, uvDelta.yzzy)	// ( 0, 2), (2, 0)
					);
				}

				if (quality > ShadowMappingQuality.HARD)
				{
					uvDelta = multiply(float4(-2, -1, 1, 2), invertSize);
					uvs.push(
						add(uv.xyxy, uvDelta.xzyw),	// (-2,  1), (-1,  2)
						add(uv.xyxy, uvDelta.zwwz),	// ( 1,  2), ( 2,  1)
						add(uv.xyxy, uvDelta.wyzx),	// ( 2, -1), ( 1, -2)
						add(uv.xyxy, uvDelta.xyyx)	// (-2, -1), (-1, -2)
					);
				}

				var numSamples : uint = uvs.length;
				for (var sampleId : uint = 0; sampleId < numSamples; sampleId += 2)
				{
					precomputedDepth = float4(
						unpack(sampleTexture(depthMap, uvs[sampleId].xy)),
						unpack(sampleTexture(depthMap, uvs[sampleId].zw)),
						unpack(sampleTexture(depthMap, uvs[sampleId + 1].xy)),
						unpack(sampleTexture(depthMap, uvs[sampleId + 1].zw))
					);

					var localNoShadows : SFloat = lessEqual(curDepthSubBias, precomputedDepth);
					noShadows.incrementBy(dotProduct4(localNoShadows, float4(1, 1, 1, 1)));
				}

				noShadows.scaleBy(1 / (2 * numSamples + 1));
			}

			return noShadows;
		}
	}
}

package aerys.minko.render.shader.part.phong.attenuation
{
	import aerys.minko.render.material.phong.PhongProperties;
	import aerys.minko.render.shader.SFloat;
	import aerys.minko.render.shader.Shader;
	import aerys.minko.render.shader.part.phong.LightAwareShaderPart;
	import aerys.minko.type.enum.SamplerFiltering;
	import aerys.minko.type.enum.SamplerMipMapping;
	import aerys.minko.type.enum.SamplerWrapping;
	import aerys.minko.type.enum.ShadowMappingQuality;

	/**
	 * Fixme, bias should be:Total bias is m*SLOPESCALE + DEPTHBIAS
	 * Where m = max( | ∂z/∂x | , | ∂z/∂y | )
	 * ftp://download.nvidia.com/developer/presentations/2004/GPU_Jackpot/Shadow_Mapping.pdf
	 *
	 * @author Romain Gilliotte
	 */
	public class MatrixShadowMapAttenuationShaderPart extends LightAwareShaderPart implements IAttenuationShaderPart
	{
		public function MatrixShadowMapAttenuationShaderPart(main : Shader)
		{
			super(main);
		}

		public function getAttenuation(lightId : uint) : SFloat
		{
			var lightType	: uint		= getLightConstant(lightId, 'type');
			var screenPos	: SFloat	= interpolate(localToScreen(fsLocalPosition));

			// retrieve shadow bias
			var shadowBias : SFloat;
			if (meshBindings.propertyExists(PhongProperties.SHADOW_BIAS))
				shadowBias = meshBindings.getParameter(PhongProperties.SHADOW_BIAS, 1);
			else
				shadowBias = getLightParameter(lightId, PhongProperties.SHADOW_BIAS, 1);

			// retrieve depthmap and projection matrix
			var worldToUV	: SFloat = getLightParameter(lightId, 'worldToUV', 16);
			var depthMap	: SFloat = getLightTextureParameter(
				lightId,
				'shadowMap',
				SamplerFiltering.NEAREST,
				SamplerMipMapping.DISABLE,
				SamplerWrapping.CLAMP
			);

			// read expected depth from shadow map, and compute current depth
			var uv : SFloat;
			uv = multiply4x4(vsWorldPosition, worldToUV);
			uv = interpolate(uv);

			var currentDepth : SFloat = uv.z;
			currentDepth = min(subtract(1, shadowBias), currentDepth);

			uv = divide(uv, uv.w);

			var outsideMap			: SFloat = notEqual(
                0,
                dotProduct4(notEqual(uv, saturate(uv)), notEqual(uv, saturate(uv)))
            );

			var precomputedDepth	: SFloat = unpack(sampleTexture(depthMap, uv.xyyy));

			var curDepthSubBias		: SFloat = subtract(currentDepth, shadowBias);
			var noShadows			: SFloat = lessEqual(curDepthSubBias, precomputedDepth);

			var quality		: uint		= getLightConstant(lightId, 'shadowQuality');

			if (quality != ShadowMappingQuality.HARD)
			{
				var invertSize	: SFloat			= divide(
					getLightParameter(lightId, 'shadowSpread', 1),
					getLightParameter(lightId, 'shadowMapSize', 1)
				);

				var uvs 		: Vector.<SFloat>	= new <SFloat>[];
				var uvDelta		: SFloat;

				if (quality > ShadowMappingQuality.LOW)
				{
					uvDelta = multiply(float3(-1, 0, 1), invertSize);
					uvs.push(
						add(uv.xyxy, uvDelta.xxxy),	// (-1, -1), (-1,  0)
						add(uv.xyxy, uvDelta.xzyx),	// (-1,  1), ( 0, -1)
						add(uv.xyxy, uvDelta.yzzx),	// ( 0,  1), ( 1, -1)
						add(uv.xyxy, uvDelta.zyzz)	// ( 1,  0), ( 1,  1)
					);
				}

				if (quality > ShadowMappingQuality.MEDIUM)
				{
					uvDelta = multiply(float3(-2, 0, 2), invertSize);
					uvs.push(
						add(uv.xyxy, uvDelta.xyyx),	// (-2, 0), (0, -2)
						add(uv.xyxy, uvDelta.yzzy)	// ( 0, 2), (2, 0)
					);
				}

				if (quality > ShadowMappingQuality.HARD)
				{
					uvDelta = multiply(float4(-2, -1, 1, 2), invertSize);
					uvs.push(
						add(uv.xyxy, uvDelta.xzyw),	// (-2,  1), (-1,  2)
						add(uv.xyxy, uvDelta.zwwz),	// ( 1,  2), ( 2,  1)
						add(uv.xyxy, uvDelta.wyzx),	// ( 2, -1), ( 1, -2)
						add(uv.xyxy, uvDelta.xyyx)	// (-2, -1), (-1, -2)
					);
				}

				var numSamples : uint = uvs.length;
				for (var sampleId : uint = 0; sampleId < numSamples; sampleId += 2)
				{
					precomputedDepth = float4(
						unpack(sampleTexture(depthMap, uvs[sampleId].xy)),
						unpack(sampleTexture(depthMap, uvs[sampleId].zw)),
						unpack(sampleTexture(depthMap, uvs[sampleId + 1].xy)),
						unpack(sampleTexture(depthMap, uvs[sampleId + 1].zw))
					);

					var localNoShadows : SFloat = lessEqual(curDepthSubBias, precomputedDepth);
					noShadows.incrementBy(dotProduct4(localNoShadows, float4(1, 1, 1, 1)));
				}

				noShadows.scaleBy(1 / (2 * numSamples + 1));
			}

			return noShadows;
		}
	}
}

use SamplerFiltering.NEAREST for shadow map sampling


////////////////////////////////////////////////////////////////////////////////
// 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.model
{

import com.esri.ags.portal.Portal;
import com.esri.builder.supportClasses.URLUtil;

import flash.events.Event;
import flash.events.EventDispatcher;

import mx.utils.StringUtil;

public class PortalModel extends EventDispatcher
{
    //--------------------------------------------------------------------------
    //
    //  Constants
    //
    //--------------------------------------------------------------------------

    public static const DEFAULT_PORTAL_URL:String = "https://www.arcgis.com";

    private static var instance:PortalModel;

    //--------------------------------------------------------------------------
    //
    //  Class methods
    //
    //--------------------------------------------------------------------------

    public static function getInstance():PortalModel
    {
        if (!instance)
        {
            instance = new PortalModel(new SingletonEnforcer());
        }

        return instance;
    }

    public function cleanUpPortalURL(url:String):String
    {
        var cleanURL:String = url;
        cleanURL = StringUtil.trim(cleanURL);
        cleanURL = replacePreviousDefaultPortalURL(cleanURL);
        cleanURL = cleanURL.replace(/\/sharing\/content\/items\/?$/i, '');
        cleanURL = cleanURL.charAt(cleanURL.length - 1) == "/" ? cleanURL : cleanURL + "/";
        cleanURL = URLUtil.encode(cleanURL);
        return cleanURL;
    }

    //--------------------------------------------------------------------------
    //
    //  Constructor
    //
    //--------------------------------------------------------------------------

    public function PortalModel(singletonEnforcer:SingletonEnforcer)
    {
        if (!singletonEnforcer)
        {
            throw new Error("Class should not be instantiated - use getInstance()");
        }
    }

    //--------------------------------------------------------------------------
    //
    //  Properties
    //
    //--------------------------------------------------------------------------

    //----------------------------------
    //  portal
    //----------------------------------

    [Bindable]
    public var portal:Portal = new Portal();

    //----------------------------------
    //  portalURL
    //----------------------------------

    /**
     * Stripped value of the _userDefinedPortalURL
     * (without "/sharing/content/items")
     */
    private var _portalURL:String;

    [Bindable("userDefinedPortalURLChanged")]
    public function get portalURL():String
    {
        return _portalURL;
    }

    public function set portalURL(value:String):void
    {
        if (_userDefinedPortalURL != value)
        {
            _userDefinedPortalURL = cleanUpPortalURL(value);
            _portalURL = _userDefinedPortalURL;
            dispatchEvent(new Event("userDefinedPortalURLChanged"));
        }
    }

    private function replacePreviousDefaultPortalURL(url:String):String
    {
        const previousDefaultPortalURL:String = "http://www.arcgis.com/";
        return url.replace(previousDefaultPortalURL, DEFAULT_PORTAL_URL);
    }

    //----------------------------------
    //  userDefinedPortalURL
    //----------------------------------
    /**
     * The ArcGIS Portal URL set by the user
     */
    private var _userDefinedPortalURL:String;

    [Bindable(event="userDefinedPortalURLChanged")]
    public function get userDefinedPortalURL():String
    {
        return _userDefinedPortalURL;
    }
}
}

class SingletonEnforcer
{
}

////////////////////////////////////////////////////////////////////////////////
// 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.model
{

import com.esri.ags.portal.Portal;
import com.esri.builder.supportClasses.URLUtil;

import flash.events.Event;
import flash.events.EventDispatcher;

import mx.utils.StringUtil;

public class PortalModel extends EventDispatcher
{
    //--------------------------------------------------------------------------
    //
    //  Constants
    //
    //--------------------------------------------------------------------------

    public static const DEFAULT_PORTAL_URL:String = "https://www.arcgis.com/";

    private static var instance:PortalModel;

    //--------------------------------------------------------------------------
    //
    //  Class methods
    //
    //--------------------------------------------------------------------------

    public static function getInstance():PortalModel
    {
        if (!instance)
        {
            instance = new PortalModel(new SingletonEnforcer());
        }

        return instance;
    }

    public function cleanUpPortalURL(url:String):String
    {
        var cleanURL:String = url;
        cleanURL = StringUtil.trim(cleanURL);
        cleanURL = replacePreviousDefaultPortalURL(cleanURL);
        cleanURL = cleanURL.replace(/\/sharing\/content\/items\/?$/i, '');
        cleanURL = cleanURL.charAt(cleanURL.length - 1) == "/" ? cleanURL : cleanURL + "/";
        cleanURL = URLUtil.encode(cleanURL);
        return cleanURL;
    }

    //--------------------------------------------------------------------------
    //
    //  Constructor
    //
    //--------------------------------------------------------------------------

    public function PortalModel(singletonEnforcer:SingletonEnforcer)
    {
        if (!singletonEnforcer)
        {
            throw new Error("Class should not be instantiated - use getInstance()");
        }
    }

    //--------------------------------------------------------------------------
    //
    //  Properties
    //
    //--------------------------------------------------------------------------

    //----------------------------------
    //  portal
    //----------------------------------

    [Bindable]
    public var portal:Portal = new Portal();

    //----------------------------------
    //  portalURL
    //----------------------------------

    /**
     * Stripped value of the _userDefinedPortalURL
     * (without "/sharing/content/items")
     */
    private var _portalURL:String;

    [Bindable("userDefinedPortalURLChanged")]
    public function get portalURL():String
    {
        return _portalURL;
    }

    public function set portalURL(value:String):void
    {
        if (_userDefinedPortalURL != value)
        {
            _userDefinedPortalURL = cleanUpPortalURL(value);
            _portalURL = _userDefinedPortalURL;
            dispatchEvent(new Event("userDefinedPortalURLChanged"));
        }
    }

    private function replacePreviousDefaultPortalURL(url:String):String
    {
        const previousDefaultPortalURL:String = "http://www.arcgis.com/";
        return url.replace(previousDefaultPortalURL, DEFAULT_PORTAL_URL);
    }

    //----------------------------------
    //  userDefinedPortalURL
    //----------------------------------
    /**
     * The ArcGIS Portal URL set by the user
     */
    private var _userDefinedPortalURL:String;

    [Bindable(event="userDefinedPortalURLChanged")]
    public function get userDefinedPortalURL():String
    {
        return _userDefinedPortalURL;
    }
}
}

class SingletonEnforcer
{
}

Update default Portal URL with trailing forward slash.


//////////////////////////////////////////////////////////////////////////////////////
//
//  Copyright 2012 Freshplanet (http://freshplanet.com | opensource@freshplanet.com)
//
//  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.freshplanet.ane.AirInAppPurchase
{
	import flash.events.EventDispatcher;
	import flash.events.StatusEvent;
	import flash.external.ExtensionContext;
	import flash.system.Capabilities;

	public class InAppPurchase extends EventDispatcher
	{
		private static var _instance:InAppPurchase;

		private var extCtx:*;

		public function InAppPurchase()
		{
			if (!_instance)
			{
				if (this.isInAppPurchaseSupported)
				{
					extCtx = ExtensionContext.createExtensionContext("com.freshplanet.AirInAppPurchase", null);
					if (extCtx != null)
					{
						extCtx.addEventListener(StatusEvent.STATUS, onStatus);
					} else
					{
						trace('[InAppPurchase] extCtx is null.');
					}
				}
			_instance = this;
			}
			else
			{
				throw Error( 'This is a singleton, use getInstance, do not call the constructor directly');
			}
		}

		public static function getInstance():InAppPurchase
		{
			return _instance != null ? _instance : new InAppPurchase();
		}

		public function init(googlePlayKey:String, debug:Boolean = false):void
		{
			if (this.isInAppPurchaseSupported)
			{
				trace("[InAppPurchase] init library");
				extCtx.call("initLib", googlePlayKey, debug);
			}
		}

		public function makePurchase(productId:String ):void
		{
			if (this.isInAppPurchaseSupported)
			{
				trace("[InAppPurchase] purchasing", productId);
				extCtx.call("makePurchase", productId);
			} else
			{
				this.dispatchEvent(new InAppPurchaseEvent(InAppPurchaseEvent.PURCHASE_ERROR, "InAppPurchase not supported"));
			}
		}

		// receipt is for android device.
		public function removePurchaseFromQueue(productId:String, receipt:String):void
		{
			if (this.isInAppPurchaseSupported)
			{
				trace("[InAppPurchase] removing product from queue", productId, receipt);
				extCtx.call("removePurchaseFromQueue", productId, receipt);

				if (Capabilities.manufacturer.indexOf("iOS") > -1)
				{
					_iosPendingPurchases = _iosPendingPurchases.filter(function(jsonPurchase:String, index:int, purchases:Vector.<Object>):Boolean {
						try
						{
							var purchase:Object = JSON.parse(jsonPurchase);
							return JSON.stringify(purchase.receipt) != receipt;
						}
						catch(error:Error)
						{
							trace("[InAppPurchase] Couldn't parse purchase: " + jsonPurchase);
						}
						return false;
					});
				}
			}
		}

		public function getProductsInfo(productsId:Array):void
		{
			if (this.isInAppPurchaseSupported)
			{
				trace("[InAppPurchase] get Products Info");
				extCtx.call("getProductsInfo", productsId);
			} else
			{
				this.dispatchEvent( new InAppPurchaseEvent(InAppPurchaseEvent.PRODUCT_INFO_ERROR) );
			}

		}

		public function userCanMakeAPurchase():void
		{
			if (this.isInAppPurchaseSupported)
			{
				trace("[InAppPurchase] check user can make a purchase");
				extCtx.call("userCanMakeAPurchase");
			} else
			{
				this.dispatchEvent(new InAppPurchaseEvent(InAppPurchaseEvent.PURCHASE_DISABLED));
			}
		}

		public function userCanMakeASubscription():void
		{
			if (Capabilities.manufacturer.indexOf('Android') > -1)
			{
				trace("[InAppPurchase] check user can make a purchase");
				extCtx.call("userCanMakeASubscription");
			} else
			{
				this.dispatchEvent(new InAppPurchaseEvent(InAppPurchaseEvent.PURCHASE_DISABLED));
			}
		}

		public function makeSubscription(productId:String):void
		{
			if (Capabilities.manufacturer.indexOf('Android') > -1)
			{
				trace("[InAppPurchase] check user can make a subscription");
				extCtx.call("makeSubscription", productId);
			} else
			{
				this.dispatchEvent(new InAppPurchaseEvent(InAppPurchaseEvent.PURCHASE_ERROR, "InAppPurchase not supported"));
			}
		}

		public function restoreTransactions():void
		{
			if (Capabilities.manufacturer.indexOf('Android') > -1)
			{
				extCtx.call("restoreTransaction");
			}
			else if (Capabilities.manufacturer.indexOf("iOS") > -1)
			{
				var jsonPurchases:String = "[" + _iosPendingPurchases.join(",") + "]";
				var jsonData:String = "{ \"purchases\": " + jsonPurchases + "}";
				dispatchEvent(new InAppPurchaseEvent(InAppPurchaseEvent.RESTORE_INFO_RECEIVED, jsonData));
			}
		}

		public function stop():void
		{
			if (Capabilities.manufacturer.indexOf('Android') > -1)
			{
				trace("[InAppPurchase] stop library");
				extCtx.call("stopLib");
			}
		}

		public function get isInAppPurchaseSupported():Boolean
		{
			var value:Boolean = Capabilities.manufacturer.indexOf('iOS') > -1 || Capabilities.manufacturer.indexOf('Android') > -1;
			trace(value ? '[InAppPurchase]  in app purchase is supported ' : '[InAppPurchase]  in app purchase is not supported ');
			return value;
		}

		private var _iosPendingPurchases:Vector.<Object> = new Vector.<Object>();

		private function onStatus(event:StatusEvent):void
		{
			trace(event);
			var e:InAppPurchaseEvent;
			switch(event.code)
			{
				case "INIT_SUCCESSFULL":
					e = new InAppPurchaseEvent(InAppPurchaseEvent.INIT_SUCCESSFULL);
					break;
				case "INIT_ERROR":
					e = new InAppPurchaseEvent(InAppPurchaseEvent.INIT_ERROR, event.level);
					break;
				case "PRODUCT_INFO_RECEIVED":
					e = new InAppPurchaseEvent(InAppPurchaseEvent.PRODUCT_INFO_RECEIVED, event.level);
					break;
				case "PURCHASE_SUCCESSFUL":
					if (Capabilities.manufacturer.indexOf("iOS") > -1)
					{
						_iosPendingPurchases.push(event.level);
					}
					e = new InAppPurchaseEvent(InAppPurchaseEvent.PURCHASE_SUCCESSFULL, event.level);
					break;
				case "PURCHASE_ERROR":
					e = new InAppPurchaseEvent(InAppPurchaseEvent.PURCHASE_ERROR, event.level);
					break;
				case "PURCHASE_ENABLED":
					e = new InAppPurchaseEvent(InAppPurchaseEvent.PURCHASE_ENABLED, event.level);
					break;
				case "PURCHASE_DISABLED":
					e = new InAppPurchaseEvent(InAppPurchaseEvent.PURCHASE_DISABLED, event.level);
					break;
				case "PRODUCT_INFO_ERROR":
					e = new InAppPurchaseEvent(InAppPurchaseEvent.PRODUCT_INFO_ERROR);
					break;
				case "SUBSCRIPTION_ENABLED":
					e = new InAppPurchaseEvent(InAppPurchaseEvent.SUBSCRIPTION_ENABLED);
					break;
				case "SUBSCRIPTION_DISABLED":
					e = new InAppPurchaseEvent(InAppPurchaseEvent.SUBSCRIPTION_DISABLED);
					break;
				case "RESTORE_INFO_RECEIVED":
					e = new InAppPurchaseEvent(InAppPurchaseEvent.RESTORE_INFO_RECEIVED, event.level);
					break;
				default:

			}
			if (e)
			{
				this.dispatchEvent(e);
			}

		}

	}
}

//////////////////////////////////////////////////////////////////////////////////////
//
//  Copyright 2012 Freshplanet (http://freshplanet.com | opensource@freshplanet.com)
//
//  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.freshplanet.ane.AirInAppPurchase
{
	import flash.events.EventDispatcher;
	import flash.events.StatusEvent;
	import flash.external.ExtensionContext;
	import flash.system.Capabilities;

	public class InAppPurchase extends EventDispatcher
	{
		private static var _instance:InAppPurchase;

		private var extCtx:*;

		public function InAppPurchase()
		{
			if (!_instance)
			{
				if (this.isInAppPurchaseSupported)
				{
					extCtx = ExtensionContext.createExtensionContext("com.freshplanet.AirInAppPurchase", null);
					if (extCtx != null)
					{
						extCtx.addEventListener(StatusEvent.STATUS, onStatus);
					} else
					{
						trace('[InAppPurchase] extCtx is null.');
					}
				}
			_instance = this;
			}
			else
			{
				throw Error( 'This is a singleton, use getInstance, do not call the constructor directly');
			}
		}

		public static function getInstance():InAppPurchase
		{
			return _instance != null ? _instance : new InAppPurchase();
		}

		public function init(googlePlayKey:String, debug:Boolean = false):void
		{
			if (this.isInAppPurchaseSupported)
			{
				trace("[InAppPurchase] init library");
				extCtx.call("initLib", googlePlayKey, debug);
			}
		}

		public function makePurchase(productId:String ):void
		{
			if (this.isInAppPurchaseSupported)
			{
				trace("[InAppPurchase] purchasing", productId);
				extCtx.call("makePurchase", productId);
			} else
			{
				this.dispatchEvent(new InAppPurchaseEvent(InAppPurchaseEvent.PURCHASE_ERROR, "InAppPurchase not supported"));
			}
		}

		// receipt is for android device.
		public function removePurchaseFromQueue(productId:String, receipt:String):void
		{
			if (this.isInAppPurchaseSupported)
			{
				trace("[InAppPurchase] removing product from queue", productId, receipt);
				extCtx.call("removePurchaseFromQueue", productId, receipt);

				if (Capabilities.manufacturer.indexOf("iOS") > -1)
				{
					_iosPendingPurchases = _iosPendingPurchases.filter(function(jsonPurchase:String, index:int, purchases:Vector.<Object>):Boolean {
						try
						{
							var purchase:Object = JSON.parse(jsonPurchase);
							return JSON.stringify(purchase.receipt) != receipt;
						}
						catch(error:Error)
						{
							trace("[InAppPurchase] Couldn't parse purchase: " + jsonPurchase);
						}
						return false;
					});
				}
			}
		}

		public function getProductsInfo(productsId:Array):void
		{
			if (this.isInAppPurchaseSupported)
			{
				trace("[InAppPurchase] get Products Info");
				extCtx.call("getProductsInfo", productsId);
			} else
			{
				this.dispatchEvent( new InAppPurchaseEvent(InAppPurchaseEvent.PRODUCT_INFO_ERROR) );
			}

		}

		public function userCanMakeAPurchase():void
		{
			if (this.isInAppPurchaseSupported)
			{
				trace("[InAppPurchase] check user can make a purchase");
				extCtx.call("userCanMakeAPurchase");
			} else
			{
				this.dispatchEvent(new InAppPurchaseEvent(InAppPurchaseEvent.PURCHASE_DISABLED));
			}
		}

		public function userCanMakeASubscription():void
		{
			if (Capabilities.manufacturer.indexOf('Android') > -1)
			{
				trace("[InAppPurchase] check user can make a purchase");
				extCtx.call("userCanMakeASubscription");
			} else
			{
				this.dispatchEvent(new InAppPurchaseEvent(InAppPurchaseEvent.PURCHASE_DISABLED));
			}
		}

		public function makeSubscription(productId:String):void
		{
			if (Capabilities.manufacturer.indexOf('Android') > -1)
			{
				trace("[InAppPurchase] check user can make a subscription");
				extCtx.call("makeSubscription", productId);
			} else
			{
				this.dispatchEvent(new InAppPurchaseEvent(InAppPurchaseEvent.PURCHASE_ERROR, "InAppPurchase not supported"));
			}
		}

		public function restoreTransactions():void
		{
			if (Capabilities.manufacturer.indexOf('Android') > -1)
			{
				extCtx.call("restoreTransaction");
			}
			else if (Capabilities.manufacturer.indexOf("iOS") > -1)
			{
				extCtx.call("restoreTransaction");

				//This restore purchases in memory not from appstore
				//var jsonPurchases:String = "[" + _iosPendingPurchases.join(",") + "]";
				//var jsonData:String = "{ \"purchases\": " + jsonPurchases + "}";
				//dispatchEvent(new InAppPurchaseEvent(InAppPurchaseEvent.RESTORE_INFO_RECEIVED, jsonData));
			}
		}

		public function stop():void
		{
			if (Capabilities.manufacturer.indexOf('Android') > -1)
			{
				trace("[InAppPurchase] stop library");
				extCtx.call("stopLib");
			}
		}

		public function get isInAppPurchaseSupported():Boolean
		{
			var value:Boolean = Capabilities.manufacturer.indexOf('iOS') > -1 || Capabilities.manufacturer.indexOf('Android') > -1;
			trace(value ? '[InAppPurchase]  in app purchase is supported ' : '[InAppPurchase]  in app purchase is not supported ');
			return value;
		}

		private var _iosPendingPurchases:Vector.<Object> = new Vector.<Object>();

		private function onStatus(event:StatusEvent):void
		{
			trace(event);
			var e:InAppPurchaseEvent;
			switch(event.code)
			{
				case "INIT_SUCCESSFULL":
					e = new InAppPurchaseEvent(InAppPurchaseEvent.INIT_SUCCESSFULL);
					break;
				case "INIT_ERROR":
					e = new InAppPurchaseEvent(InAppPurchaseEvent.INIT_ERROR, event.level);
					break;
				case "PRODUCT_INFO_RECEIVED":
					e = new InAppPurchaseEvent(InAppPurchaseEvent.PRODUCT_INFO_RECEIVED, event.level);
					break;
				case "PURCHASE_SUCCESSFUL":
					if (Capabilities.manufacturer.indexOf("iOS") > -1)
					{
						_iosPendingPurchases.push(event.level);
					}
					e = new InAppPurchaseEvent(InAppPurchaseEvent.PURCHASE_SUCCESSFULL, event.level);
					break;
				case "PURCHASE_ERROR":
					e = new InAppPurchaseEvent(InAppPurchaseEvent.PURCHASE_ERROR, event.level);
					break;
				case "PURCHASE_ENABLED":
					e = new InAppPurchaseEvent(InAppPurchaseEvent.PURCHASE_ENABLED, event.level);
					break;
				case "PURCHASE_DISABLED":
					e = new InAppPurchaseEvent(InAppPurchaseEvent.PURCHASE_DISABLED, event.level);
					break;
				case "PRODUCT_INFO_ERROR":
					e = new InAppPurchaseEvent(InAppPurchaseEvent.PRODUCT_INFO_ERROR);
					break;
				case "SUBSCRIPTION_ENABLED":
					e = new InAppPurchaseEvent(InAppPurchaseEvent.SUBSCRIPTION_ENABLED);
					break;
				case "SUBSCRIPTION_DISABLED":
					e = new InAppPurchaseEvent(InAppPurchaseEvent.SUBSCRIPTION_DISABLED);
					break;
				case "RESTORE_INFO_RECEIVED":
					e = new InAppPurchaseEvent(InAppPurchaseEvent.RESTORE_INFO_RECEIVED, event.level);
					break;
				default:

			}
			if (e)
			{
				this.dispatchEvent(e);
			}

		}

	}
}

Update InAppPurchase.as

Update InAppPurchase.as

Restore from appstore not from memory