package aerys.minko.render.resource
{
	import flash.display.BitmapData;
	import flash.display3D.Context3D;
	import flash.display3D.IndexBuffer3D;
	import flash.display3D.Program3D;
	import flash.display3D.VertexBuffer3D;
	import flash.display3D.textures.CubeTexture;
	import flash.display3D.textures.Texture;
	import flash.display3D.textures.TextureBase;
	import flash.geom.Rectangle;

	import aerys.minko.ns.minko_render;

	public final class Context3DResource
	{
		private var _context				                : Context3D;

		private var _enableErrorChecking	                : Boolean;

		private var _rttTarget			                	: TextureBase;
		private var _rttDepthAndStencil	                	: Boolean;
		private var _rttAntiAliasing	                	: int;
		private var _rttSurfaceSelector		                : int;
		private var _rectangle				                : Rectangle;

		private var _depthMask			                	: Boolean;
		private var _passCompareMode	                	: String;

		private var _program			                	: Program3D;

		private var _blendingSource		                	: String;
		private var _blendingDestination                	: String;

		private var _triangleCulling	                	: String;

		private var _vertexBuffers			                : Vector.<VertexBuffer3D>;
		private var _vertexBuffersOffsets	                : Vector.<int>;
		private var _vertexBuffersFormats	                : Vector.<String>;
		private var _textures				                : Vector.<TextureBase>;

		private var _colorMaskRed			                : Boolean;
		private var _colorMaskGreen			                : Boolean;
		private var _colorMaskBlue			                : Boolean;
		private var _colorMaskAlpha	                		: Boolean;

        private var _stencilRefNum                          : uint;
        private var _stencilReadMask                        : uint;
        private var _stencilWriteMask                       : uint;
        private var _stencilTriangleFace                    : String;
        private var _stencilCompareMode                     : String;
        private var _stencilActionOnBothPass                : String;
        private var _stencilActionOnDepthFail               : String;
        private var _stencilActionOnDepthPassStencilFail    : String;

		minko_render function get context() : Context3D
		{
			return _context;
		}

		public function get enabledErrorChecking() : Boolean
		{
			return _enableErrorChecking;
		}
		public function set enableErrorChecking(value : Boolean) : void
		{
			if (value != _enableErrorChecking)
			{
				_enableErrorChecking = value;
				_context.enableErrorChecking = value;
			}
		}

		public function Context3DResource(context : Context3D)
		{
			_context = context;

            initialize();
		}

        private function initialize() : void
        {
            _vertexBuffers = new Vector.<VertexBuffer3D>(8, true);
            _vertexBuffersOffsets = new Vector.<int>(8, true);
            _vertexBuffersFormats = new Vector.<String>(8, true);
            _textures = new Vector.<TextureBase>(8, true);
        }

		public function clear(red		: Number	= 0.0,
							  green		: Number	= 0.0,
							  blue		: Number	= 0.0,
							  alpha		: Number	= 1.0,
							  depth		: Number	= 1.0,
							  stencil	: uint		= 0,
							  mask		: uint		= 0xffffffff) : void
		{
			_context.clear(red, green, blue, alpha, depth, stencil, mask);
		}

		public function configureBackBuffer(width					: int,
											height					: int,
											antiAlias				: int,
											enabledDepthAndStencil	: Boolean) : Context3DResource
		{
			_context.configureBackBuffer(width, height, antiAlias, enabledDepthAndStencil);

			return this;
		}

		public function createCubeTexture(size							: int,
										  format						: String,
										  optimizeForRenderToTexture	: Boolean) : CubeTexture
		{
			return _context.createCubeTexture(size, format, optimizeForRenderToTexture);
		}

		public function createIndexBuffer(numIndices : int) : IndexBuffer3D
		{
			return _context.createIndexBuffer(numIndices);
		}

		public function createProgram() : Program3D
		{
			return _context.createProgram();
		}

		public function createTexture(width							: int,
									  height						: int,
									  format						: String,
									  optimizeForRenderToTexture	: Boolean) : Texture
		{
			return _context.createTexture(width, height, format, optimizeForRenderToTexture);
		}

		public function createVertexBuffer(numVertices : int, data32PerVertex : int) : VertexBuffer3D
		{
			return _context.createVertexBuffer(numVertices, data32PerVertex);
		}

		public function dispose() : void
		{
			_context.dispose();
		}

		public function drawToBitmapData(destination : BitmapData) : Context3DResource
		{
			_context.drawToBitmapData(destination);

			return this;
		}

		public function drawTriangles(indexBuffer	: IndexBuffer3D,
									  firstIndex	: int	= 0,
									  numTriangles	: int	= -1) : Context3DResource
		{
			_context.drawTriangles(indexBuffer, firstIndex, numTriangles);

			return this;
		}

		public function present() : Context3DResource
		{
			_context.present();

			return this;
		}

		public function setBlendFactors(source : String, destination : String) : Context3DResource
		{
			if (source != _blendingSource || destination != _blendingDestination)
			{
				_blendingSource = source;
				_blendingDestination = destination;
				_context.setBlendFactors(_blendingSource, _blendingDestination);
			}

			return this;
		}

		public function setColorMask(red 	: Boolean,
									 green 	: Boolean,
									 blue 	: Boolean,
									 alpha 	: Boolean) : Context3DResource
		{
			if (red != _colorMaskRed || green != _colorMaskGreen
				|| blue != _colorMaskBlue || alpha != _colorMaskAlpha)
			{
				_colorMaskRed = red;
				_colorMaskGreen = green;
				_colorMaskBlue = blue;
				_colorMaskAlpha = alpha;
				_context.setColorMask(
					_colorMaskRed,
					_colorMaskGreen,
					_colorMaskBlue,
					_colorMaskAlpha
				);
			}

			return this;
		}

		public function setCulling(triangleCulling : String) : Context3DResource
		{
			if (triangleCulling != _triangleCulling)
			{
				_triangleCulling = triangleCulling;
				_context.setCulling(triangleCulling);
			}

			return this;
		}

		public function setDepthTest(depthMask : Boolean, passCompareMode : String) : Context3DResource
		{
			if (depthMask != _depthMask || passCompareMode != _passCompareMode)
			{
				_depthMask = depthMask;
				_passCompareMode = passCompareMode;
				_context.setDepthTest(_depthMask, _passCompareMode);
			}

			return this;
		}

		public function setProgram(program : Program3D) : Context3DResource
		{
			if (_program != program)
			{
				_program = program;
				_context.setProgram(program);
			}

			return this;
		}

		public function setProgramConstantsFromVector(programeType	: String,
													  offset		: uint,
													  values		: Vector.<Number>,
													  numRegisters	: int	= -1) : Context3DResource
		{
			_context.setProgramConstantsFromVector(programeType, offset, values, numRegisters);

			return this;
		}

		public function setRenderToBackBuffer() : Context3DResource
		{
			if (_rttTarget != null)
			{
				_rttTarget = null;
				_context.setRenderToBackBuffer();
			}

			return this;
		}

		public function setRenderToTexture(texture					: TextureBase,
										   enableDepthAndStencil	: Boolean	= false,
										   antiAlias				: int		= 0,
										   surfaceSelector			: int		= 0) : Context3DResource
		{
			if (texture != _rttTarget || enableDepthAndStencil != _rttDepthAndStencil
				|| antiAlias != _rttAntiAliasing || surfaceSelector != _rttSurfaceSelector)
			{
				_rttTarget = texture;
				_rttDepthAndStencil = enableDepthAndStencil;
				_rttAntiAliasing = antiAlias;
				_rttSurfaceSelector = surfaceSelector;
				_context.setRenderToTexture(
					texture,
					enableDepthAndStencil,
					antiAlias,
					surfaceSelector
				);
			}

			return this;
		}

		public function setScissorRectangle(rectangle : Rectangle) : Context3DResource
		{
			if (_rectangle != rectangle)
			{
				_rectangle = rectangle;
				_context.setScissorRectangle(_rectangle);
			}

			return this;
		}

		public function setTextureAt(sampler	: uint,
									 texture	: TextureBase) : Context3DResource
		{
			if (_textures[sampler] != texture)
			{
				_textures[sampler] = texture;
				_context.setTextureAt(sampler, texture);
			}

			return this;
		}

		public function setVertexBufferAt(index			: int,
										  vertexBuffer	: VertexBuffer3D,
										  bufferOffset	: int		= 0,
										  format		: String	= 'float4') : Context3DResource
		{
			if (_vertexBuffers[index] != vertexBuffer
				|| _vertexBuffersOffsets[index] != bufferOffset
				|| _vertexBuffersFormats[index] != format)
			{
				_vertexBuffers[index] = vertexBuffer;
				_vertexBuffersOffsets[index] = bufferOffset;
				_vertexBuffersFormats[index] = format;

				_context.setVertexBufferAt(
					index,
					vertexBuffer,
					bufferOffset,
					format
				);
			}

			return this;
		}

		public function setStencilReferenceValue(refNum		: uint 	= 0,
												 readMask	: uint 	= 255,
												 writeMask	: uint 	= 255) : Context3DResource
		{
            if (refNum != _stencilRefNum
                || readMask != _stencilReadMask
                || writeMask != _stencilWriteMask
                )
            {
                _context.setStencilReferenceValue(refNum, readMask, writeMask);

                _stencilRefNum = refNum;
                _stencilReadMask = readMask;
                _stencilWriteMask = writeMask;
            }

			return this;
		}

		public function setStencilActions(triangleFace					: String = 'frontAndBack',
										  compareMode					: String = 'always',
										  actionOnBothPass				: String = 'keep',
										  actionOnDepthFail				: String = 'keep',
										  actionOnDepthPassStencilFail	: String = 'keep') : Context3DResource
		{
            if (triangleFace != _stencilTriangleFace
                || compareMode != _stencilCompareMode
                || actionOnBothPass != _stencilActionOnBothPass
                || actionOnDepthFail != _stencilActionOnDepthFail
                || actionOnDepthPassStencilFail != _stencilActionOnDepthPassStencilFail
            )
            {
    			_context.setStencilActions(
    				triangleFace,
    				compareMode,
    				actionOnBothPass,
    				actionOnDepthFail,
    				actionOnDepthPassStencilFail
    			);

                _stencilTriangleFace = triangleFace;
                _stencilCompareMode = compareMode;
                _stencilActionOnBothPass = actionOnBothPass;
                _stencilActionOnDepthFail = actionOnDepthFail;
                _stencilActionOnDepthPassStencilFail = actionOnDepthPassStencilFail;
            }

			return this;
		}

	}
}

package aerys.minko.render.resource
{
	import flash.display.BitmapData;
	import flash.display3D.Context3D;
	import flash.display3D.IndexBuffer3D;
	import flash.display3D.Program3D;
	import flash.display3D.VertexBuffer3D;
	import flash.display3D.textures.CubeTexture;
	import flash.display3D.textures.Texture;
	import flash.display3D.textures.TextureBase;
	import flash.geom.Rectangle;

	import aerys.minko.ns.minko_render;

	public final class Context3DResource
	{
		private var _context				                : Context3D;

		private var _enableErrorChecking	                : Boolean;

		private var _rttTarget			                	: TextureBase;
		private var _rttDepthAndStencil	                	: Boolean;
		private var _rttAntiAliasing	                	: int;
		private var _rttSurfaceSelector		                : int;
		private var _rectangle				                : Rectangle;

		private var _depthMask			                	: Boolean;
		private var _passCompareMode	                	: String;

		private var _program			                	: Program3D;

		private var _blendingSource		                	: String;
		private var _blendingDestination                	: String;

		private var _triangleCulling	                	: String;

		private var _vertexBuffers			                : Vector.<VertexBuffer3D>;
		private var _vertexBuffersOffsets	                : Vector.<int>;
		private var _vertexBuffersFormats	                : Vector.<String>;
		private var _textures				                : Vector.<TextureBase>;

		private var _colorMaskRed			                : Boolean;
		private var _colorMaskGreen			                : Boolean;
		private var _colorMaskBlue			                : Boolean;
		private var _colorMaskAlpha	                		: Boolean;

        private var _stencilRefNum                          : uint;
        private var _stencilReadMask                        : uint;
        private var _stencilWriteMask                       : uint;
        private var _stencilTriangleFace                    : String;
        private var _stencilCompareMode                     : String;
        private var _stencilActionOnBothPass                : String;
        private var _stencilActionOnDepthFail               : String;
        private var _stencilActionOnDepthPassStencilFail    : String;

		public function get context() : Context3D
		{
			return _context;
		}

		public function get enabledErrorChecking() : Boolean
		{
			return _enableErrorChecking;
		}
		public function set enableErrorChecking(value : Boolean) : void
		{
			if (value != _enableErrorChecking)
			{
				_enableErrorChecking = value;
				_context.enableErrorChecking = value;
			}
		}

		public function Context3DResource(context : Context3D)
		{
			_context = context;

            initialize();
		}

        private function initialize() : void
        {
            _vertexBuffers = new Vector.<VertexBuffer3D>(8, true);
            _vertexBuffersOffsets = new Vector.<int>(8, true);
            _vertexBuffersFormats = new Vector.<String>(8, true);
            _textures = new Vector.<TextureBase>(8, true);
        }

		public function clear(red		: Number	= 0.0,
							  green		: Number	= 0.0,
							  blue		: Number	= 0.0,
							  alpha		: Number	= 1.0,
							  depth		: Number	= 1.0,
							  stencil	: uint		= 0,
							  mask		: uint		= 0xffffffff) : void
		{
			_context.clear(red, green, blue, alpha, depth, stencil, mask);
		}

		public function configureBackBuffer(width					: int,
											height					: int,
											antiAlias				: int,
											enabledDepthAndStencil	: Boolean) : Context3DResource
		{
			_context.configureBackBuffer(width, height, antiAlias, enabledDepthAndStencil);

			return this;
		}

		public function createCubeTexture(size							: int,
										  format						: String,
										  optimizeForRenderToTexture	: Boolean) : CubeTexture
		{
			return _context.createCubeTexture(size, format, optimizeForRenderToTexture);
		}

		public function createIndexBuffer(numIndices : int) : IndexBuffer3D
		{
			return _context.createIndexBuffer(numIndices);
		}

		public function createProgram() : Program3D
		{
			return _context.createProgram();
		}

		public function createTexture(width							: int,
									  height						: int,
									  format						: String,
									  optimizeForRenderToTexture	: Boolean) : Texture
		{
			return _context.createTexture(width, height, format, optimizeForRenderToTexture);
		}

		public function createVertexBuffer(numVertices : int, data32PerVertex : int) : VertexBuffer3D
		{
			return _context.createVertexBuffer(numVertices, data32PerVertex);
		}

		public function dispose() : void
		{
			_context.dispose();
		}

		public function drawToBitmapData(destination : BitmapData) : Context3DResource
		{
			_context.drawToBitmapData(destination);

			return this;
		}

		public function drawTriangles(indexBuffer	: IndexBuffer3D,
									  firstIndex	: int	= 0,
									  numTriangles	: int	= -1) : Context3DResource
		{
			_context.drawTriangles(indexBuffer, firstIndex, numTriangles);

			return this;
		}

		public function present() : Context3DResource
		{
			_context.present();

			return this;
		}

		public function setBlendFactors(source : String, destination : String) : Context3DResource
		{
			if (source != _blendingSource || destination != _blendingDestination)
			{
				_blendingSource = source;
				_blendingDestination = destination;
				_context.setBlendFactors(_blendingSource, _blendingDestination);
			}

			return this;
		}

		public function setColorMask(red 	: Boolean,
									 green 	: Boolean,
									 blue 	: Boolean,
									 alpha 	: Boolean) : Context3DResource
		{
			if (red != _colorMaskRed || green != _colorMaskGreen
				|| blue != _colorMaskBlue || alpha != _colorMaskAlpha)
			{
				_colorMaskRed = red;
				_colorMaskGreen = green;
				_colorMaskBlue = blue;
				_colorMaskAlpha = alpha;
				_context.setColorMask(
					_colorMaskRed,
					_colorMaskGreen,
					_colorMaskBlue,
					_colorMaskAlpha
				);
			}

			return this;
		}

		public function setCulling(triangleCulling : String) : Context3DResource
		{
			if (triangleCulling != _triangleCulling)
			{
				_triangleCulling = triangleCulling;
				_context.setCulling(triangleCulling);
			}

			return this;
		}

		public function setDepthTest(depthMask : Boolean, passCompareMode : String) : Context3DResource
		{
			if (depthMask != _depthMask || passCompareMode != _passCompareMode)
			{
				_depthMask = depthMask;
				_passCompareMode = passCompareMode;
				_context.setDepthTest(_depthMask, _passCompareMode);
			}

			return this;
		}

		public function setProgram(program : Program3D) : Context3DResource
		{
			if (_program != program)
			{
				_program = program;
				_context.setProgram(program);
			}

			return this;
		}

		public function setProgramConstantsFromVector(programeType	: String,
													  offset		: uint,
													  values		: Vector.<Number>,
													  numRegisters	: int	= -1) : Context3DResource
		{
			_context.setProgramConstantsFromVector(programeType, offset, values, numRegisters);

			return this;
		}

		public function setRenderToBackBuffer() : Context3DResource
		{
			if (_rttTarget != null)
			{
				_rttTarget = null;
				_context.setRenderToBackBuffer();
			}

			return this;
		}

		public function setRenderToTexture(texture					: TextureBase,
										   enableDepthAndStencil	: Boolean	= false,
										   antiAlias				: int		= 0,
										   surfaceSelector			: int		= 0) : Context3DResource
		{
			if (texture != _rttTarget || enableDepthAndStencil != _rttDepthAndStencil
				|| antiAlias != _rttAntiAliasing || surfaceSelector != _rttSurfaceSelector)
			{
				_rttTarget = texture;
				_rttDepthAndStencil = enableDepthAndStencil;
				_rttAntiAliasing = antiAlias;
				_rttSurfaceSelector = surfaceSelector;
				_context.setRenderToTexture(
					texture,
					enableDepthAndStencil,
					antiAlias,
					surfaceSelector
				);
			}

			return this;
		}

		public function setScissorRectangle(rectangle : Rectangle) : Context3DResource
		{
			if (_rectangle != rectangle)
			{
				_rectangle = rectangle;
				_context.setScissorRectangle(_rectangle);
			}

			return this;
		}

		public function setTextureAt(sampler	: uint,
									 texture	: TextureBase) : Context3DResource
		{
			if (_textures[sampler] != texture)
			{
				_textures[sampler] = texture;
				_context.setTextureAt(sampler, texture);
			}

			return this;
		}

		public function setVertexBufferAt(index			: int,
										  vertexBuffer	: VertexBuffer3D,
										  bufferOffset	: int		= 0,
										  format		: String	= 'float4') : Context3DResource
		{
			if (_vertexBuffers[index] != vertexBuffer
				|| _vertexBuffersOffsets[index] != bufferOffset
				|| _vertexBuffersFormats[index] != format)
			{
				_vertexBuffers[index] = vertexBuffer;
				_vertexBuffersOffsets[index] = bufferOffset;
				_vertexBuffersFormats[index] = format;

				_context.setVertexBufferAt(
					index,
					vertexBuffer,
					bufferOffset,
					format
				);
			}

			return this;
		}

		public function setStencilReferenceValue(refNum		: uint 	= 0,
												 readMask	: uint 	= 255,
												 writeMask	: uint 	= 255) : Context3DResource
		{
            if (refNum != _stencilRefNum
                || readMask != _stencilReadMask
                || writeMask != _stencilWriteMask
                )
            {
                _context.setStencilReferenceValue(refNum, readMask, writeMask);

                _stencilRefNum = refNum;
                _stencilReadMask = readMask;
                _stencilWriteMask = writeMask;
            }

			return this;
		}

		public function setStencilActions(triangleFace					: String = 'frontAndBack',
										  compareMode					: String = 'always',
										  actionOnBothPass				: String = 'keep',
										  actionOnDepthFail				: String = 'keep',
										  actionOnDepthPassStencilFail	: String = 'keep') : Context3DResource
		{
            if (triangleFace != _stencilTriangleFace
                || compareMode != _stencilCompareMode
                || actionOnBothPass != _stencilActionOnBothPass
                || actionOnDepthFail != _stencilActionOnDepthFail
                || actionOnDepthPassStencilFail != _stencilActionOnDepthPassStencilFail
            )
            {
    			_context.setStencilActions(
    				triangleFace,
    				compareMode,
    				actionOnBothPass,
    				actionOnDepthFail,
    				actionOnDepthPassStencilFail
    			);

                _stencilTriangleFace = triangleFace;
                _stencilCompareMode = compareMode;
                _stencilActionOnBothPass = actionOnBothPass;
                _stencilActionOnDepthFail = actionOnDepthFail;
                _stencilActionOnDepthPassStencilFail = actionOnDepthPassStencilFail;
            }

			return this;
		}

	}
}

Fix context3D getter

Fix context3D getter


package org.postgresql.util {

    /**
     * Format the template by substituting any parameter marker with its
     * corresponding argument. Parameter markers are defined by a number
     * (indicating the argument index to replace) enclosed by curly braces.
     * E.g., an invocation such as format("{1} {0}!", 'world', 'hello')
     * would return the string hello world!.
     * @param template String in which to perform parameter substitution
     * @param args arguments to substitute
     */
    public function format(template:String, ...args):String {
        return template.replace(/{(\d+)}/g, function():String {
            var replacementIndex:int = int(arguments[1]);
            return replacementIndex < args.length ?
                args[replacementIndex] :
                arguments[0];
        });
    }
}

package org.postgresql.util {

    /**
     * Format the template by substituting any parameter marker with its
     * corresponding argument. Parameter markers are defined by a number
     * (indicating the argument index to replace) enclosed by curly braces.
     *

     * E.g., an invocation such as format("{1} {0}!", 'world', 'hello')
     * would return the string hello world!.
     *

     * If the replacement values are not Strings, they are coerced to
     * Strings before substitution, as per the String function.
     *
     * @param template String in which to perform parameter substitution
     * @param args arguments to substitute
     * @return a String with all token references subtituted with their corresponding values
     * @throws ArgumentException if more values are referenced than provided
     * @see String
     */
    public function format(template:String, ...args):String {
        return template.replace(/{(\d+)}/g, function():String {
            var replacementIndex:int = int(arguments[1]);
            if (replacementIndex > args.length) {
                throw new ArgumentError("Invalid log argument index: " + replacementIndex);
            } else {
                return String(args[replacementIndex]);
            }
        });
    }
}

Format method cleanup.

Format method cleanup.


/*
* Copyright (c) 2015 Snowplow Analytics Ltd. All rights reserved.
*
* This program is licensed to you under the Apache License Version 2.0,
* and you may not use this file except in compliance with the Apache License Version 2.0.
* You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the Apache License Version 2.0 is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
*/

package com.snowplowanalytics.snowplow.tracker
{
	import com.snowplowanalytics.snowplow.tracker.emitter.Emitter;
	import com.snowplowanalytics.snowplow.tracker.payload.IPayload;
	import com.snowplowanalytics.snowplow.tracker.payload.SchemaPayload;
	import com.snowplowanalytics.snowplow.tracker.payload.TrackerPayload;
	import com.snowplowanalytics.snowplow.tracker.util.Preconditions;

	import flash.display.Stage;
	import flash.external.ExternalInterface;
	import flash.net.LocalConnection;
	import flash.net.SharedObject;
	import flash.system.Capabilities;

	public class Tracker
	{
		private var base64Encoded:Boolean = true;
		private var emitter:Emitter;
		private var platform:String;
		private var appId:String;
		private var namespace:String;
		private var trackerVersion:String;
		private var subject:Subject;
		private var stage:Stage;

		private var playerType:String;
		private var playerVersion:String;
		private var isDebugger:Boolean;
		private var hasLocalStorage:Boolean;
		private var hasScriptAccess:Boolean;

		/**
		 * @param emitter Emitter to which events will be sent
		 * @param subject Subject to be tracked
		 * @param namespace Identifier for the Tracker instance
		 * @param appId Application ID
		 * @param stage The flash stage object.  used for adding stage info to payload.
		 * @param base64Encoded Whether JSONs in the payload should be base-64 encoded
		 */
		function Tracker(emitter:Emitter, subject:Subject, namespace:String, appId:String, stage:Stage = null, base64Encoded:Boolean = true) {
				this.emitter = emitter;
				this.appId = appId;
				this.base64Encoded = base64Encoded;
				this.namespace = namespace;
				this.subject = subject;
				this.trackerVersion = Version.TRACKER;
				this.platform = DevicePlatform.DESKTOP;
				this.stage = stage;

				this.playerType = Capabilities.playerType;
				this.playerVersion = Capabilities.version;
				this.isDebugger = Capabilities.isDebugger;

				try
				{
					SharedObject.getLocal("test");
					this.hasLocalStorage = true;
				}
				catch (e:Error)
				{
					this.hasLocalStorage = false;
				}
				this.hasScriptAccess = ExternalInterface.available;
			}

		/**
		 * @param payload Payload builder
		 * @param context Custom context for the event
		 * @param timestamp Optional user-provided timestamp for the event
		 * @return A completed Payload
		 */
		protected function completePayload(payload:IPayload,
										   context:Array,
										   timestamp:Number):IPayload {

				payload.add(Parameter.PLATFORM, this.platform);
				payload.add(Parameter.APPID, this.appId);
				payload.add(Parameter.NAMESPACE, this.namespace);
				payload.add(Parameter.TRACKER_VERSION, this.trackerVersion);
				payload.add(Parameter.EID, Util.getEventId());

				// If timestamp is set to 0, generate one
				payload.add(Parameter.TIMESTAMP,
					(timestamp == 0 ? Util.getTimestamp() : String(timestamp)));

				// Add flash information
				if (context == null) {
					context = [];
				}

				var flashData:TrackerPayload = new TrackerPayload();
				flashData.add(Parameter.FLASH_PLAYER_TYPE, playerType);
				flashData.add(Parameter.FLASH_VERSION, playerVersion);
				flashData.add(Parameter.FLASH_IS_DEBUGGER, isDebugger);
				flashData.add(Parameter.FLASH_HAS_LOCAL_STORAGE, hasLocalStorage);
				flashData.add(Parameter.FLASH_HAS_SCRIPT_ACCESS, hasScriptAccess);
				if (stage != null) {
					flashData.add(Parameter.FLASH_STAGE_SIZE, { "width": stage.stageWidth, "height": stage.stageHeight});
				}

				var flashPayload:SchemaPayload = new SchemaPayload();
				flashPayload.setSchema(Constants.SCHEMA_FLASH);
				flashPayload.setData(flashData.getMap());

				context.push(flashPayload);

				// Encodes context data
				if (context != null && context.length > 0) {
					var envelope:SchemaPayload = new SchemaPayload();
					envelope.setSchema(Constants.SCHEMA_CONTEXTS);

					// We can do better here, rather than re-iterate through the list
					var contextDataList:Array = [];
					for each(var schemaPayload:SchemaPayload in context) {
						contextDataList.push(schemaPayload.getMap());
					}

					envelope.setData(contextDataList);
					payload.addMap(envelope.getMap(), this.base64Encoded, Parameter.CONTEXT_ENCODED, Parameter.CONTEXT);
				}

				if (this.subject != null) {
					payload.addMap(Util.copyObject(subject.getSubject(), true));
				}

				return payload;
			}

		public function setPlatform(platform:String):void {
			this.platform = platform;
		}

		public function getPlatform():String {
			return this.platform;
		}

		protected function setTrackerVersion(version:String):void {
			this.trackerVersion = version;
		}

		private function addTrackerPayload(payload:IPayload):void {
			this.emitter.addToBuffer(payload);
		}

		public function setSubject(subject:Subject):void {
			this.subject = subject;
		}

		public function getSubject():Subject {
			return this.subject;
		}

		/**
		 * @param pageUrl URL of the viewed page
		 * @param pageTitle Title of the viewed page
		 * @param referrer Referrer of the page
		 * @param context Custom context for the event
		 * @param timestamp Optional user-provided timestamp for the event
		 */
		public function trackPageView(pageUrl:String, pageTitle:String, referrer:String, context:Array = null, timestamp:Number = 0):void
		{
			// Precondition checks
			Preconditions.checkNotNull(pageUrl);
			Preconditions.checkArgument(!Util.isNullOrEmpty(pageUrl), "pageUrl cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(pageTitle), "pageTitle cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(referrer), "referrer cannot be empty");

			var payload:IPayload = new TrackerPayload();
			payload.add(Parameter.EVENT, Constants.EVENT_PAGE_VIEW);
			payload.add(Parameter.PAGE_URL, pageUrl);
			payload.add(Parameter.PAGE_TITLE, pageTitle);
			payload.add(Parameter.PAGE_REFR, referrer);

			completePayload(payload, context, timestamp);

			addTrackerPayload(payload);
		}

		/**
		 * @param category Category of the event
		 * @param action The event itself
		 * @param label Refer to the object the action is performed on
		 * @param property Property associated with either the action or the object
		 * @param value A value associated with the user action
		 * @param context Custom context for the event
		 * @param timestamp Optional user-provided timestamp for the event
		 */
		public function trackStructuredEvent(category:String,
			action:String,
			label:String,
			property:String,
			value:int,
			context:Array = null,
			timestamp:Number = 0):void
		{
			// Precondition checks
			Preconditions.checkNotNull(label);
			Preconditions.checkNotNull(property);
			Preconditions.checkArgument(!Util.isNullOrEmpty(label), "label cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(property), "property cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(category), "category cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(action), "action cannot be empty");

			var payload:IPayload = new TrackerPayload();
			payload.add(Parameter.EVENT, Constants.EVENT_STRUCTURED);
			payload.add(Parameter.SE_CATEGORY, category);
			payload.add(Parameter.SE_ACTION, action);
			payload.add(Parameter.SE_LABEL, label);
			payload.add(Parameter.SE_PROPERTY, property);
			payload.add(Parameter.SE_VALUE, String(value));

			completePayload(payload, context, timestamp);

			addTrackerPayload(payload);
		}

		/**
		 *
		 * @param eventData The properties of the event. Has two field:
		 *                   A "data" field containing the event properties and
		 *                   A "schema" field identifying the schema against which the data is validated
		 * @param context Custom context for the event
		 * @param timestamp Optional user-provided timestamp for the event
		 */
		public function trackUnstructuredEvent(eventData:SchemaPayload, context:Array = null,
			timestamp:Number = 0):void
		{
			var payload:IPayload = new TrackerPayload();
			var envelope:SchemaPayload = new SchemaPayload();

			envelope.setSchema(Constants.SCHEMA_UNSTRUCT_EVENT);
			envelope.setData(eventData.getMap());

			payload.add(Parameter.EVENT, Constants.EVENT_UNSTRUCTURED);
			payload.addMap(envelope.getMap());

			completePayload(payload, context, timestamp);

			addTrackerPayload(payload);
		}

		/**
		 * This is an internal method called by track_ecommerce_transaction. It is not for public use.
		 * @param order_id Order ID
		 * @param sku Item SKU
		 * @param price Item price
		 * @param quantity Item quantity
		 * @param name Item name
		 * @param category Item category
		 * @param currency The currency the price is expressed in
		 * @param context Custom context for the event
		 * @param timestamp Optional user-provided timestamp for the event
		 */
		protected function trackEcommerceTransactionItem(order_id:String, sku:String, price:Number,
			quantity:int, name:String, category:String,
			currency:String, context:Array,
			timestamp:Number):void
		{
			// Precondition checks
			Preconditions.checkNotNull(name);
			Preconditions.checkNotNull(category);
			Preconditions.checkNotNull(currency);
			Preconditions.checkArgument(!Util.isNullOrEmpty(order_id), "order_id cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(sku), "sku cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(name), "name cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(category), "category cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(currency), "currency cannot be empty");

			var payload:IPayload = new TrackerPayload();
			payload.add(Parameter.EVENT, Constants.EVENT_ECOMM_ITEM);
			payload.add(Parameter.TI_ITEM_ID, order_id);
			payload.add(Parameter.TI_ITEM_SKU, sku);
			payload.add(Parameter.TI_ITEM_NAME, name);
			payload.add(Parameter.TI_ITEM_CATEGORY, category);
			payload.add(Parameter.TI_ITEM_PRICE, String(price));
			payload.add(Parameter.TI_ITEM_QUANTITY, String(quantity));
			payload.add(Parameter.TI_ITEM_CURRENCY, currency);

			completePayload(payload, context, timestamp);

			addTrackerPayload(payload);
		}

		/**
		 * @param order_id ID of the eCommerce transaction
		 * @param total_value Total transaction value
		 * @param affiliation Transaction affiliation
		 * @param tax_value Transaction tax value
		 * @param shipping Delivery cost charged
		 * @param city Delivery address city
		 * @param state Delivery address state
		 * @param country Delivery address country
		 * @param currency The currency the price is expressed in
		 * @param items The items in the transaction
		 * @param context Custom context for the event
		 * @param timestamp Optional user-provided timestamp for the event
		 */
		public function trackEcommerceTransaction(order_id:String, total_value:Number, affiliation:String,
			tax_value:Number, shipping:Number, city:String,
			state:String, country:String, currency:String,
			items:Array, context:Array = null,
			timestamp:Number = 0):void
		{
			// Precondition checks
			Preconditions.checkNotNull(affiliation);
			Preconditions.checkNotNull(city);
			Preconditions.checkNotNull(state);
			Preconditions.checkNotNull(country);
			Preconditions.checkNotNull(currency);
			Preconditions.checkArgument(!Util.isNullOrEmpty(order_id), "order_id cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(affiliation), "affiliation cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(city), "city cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(state), "state cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(country), "country cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(currency), "currency cannot be empty");

			var payload:IPayload = new TrackerPayload();
			payload.add(Parameter.EVENT, Constants.EVENT_ECOMM);
			payload.add(Parameter.TR_ID, order_id);
			payload.add(Parameter.TR_TOTAL, String(total_value));
			payload.add(Parameter.TR_AFFILIATION, affiliation);
			payload.add(Parameter.TR_TAX, String(tax_value));
			payload.add(Parameter.TR_SHIPPING, String(shipping));
			payload.add(Parameter.TR_CITY, city);
			payload.add(Parameter.TR_STATE, state);
			payload.add(Parameter.TR_COUNTRY, country);
			payload.add(Parameter.TR_CURRENCY, currency);

			completePayload(payload, context, timestamp);

			for each(var item:TransactionItem in items) {
				trackEcommerceTransactionItem(
					String(item.get(Parameter.TI_ITEM_ID)),
					String(item.get(Parameter.TI_ITEM_SKU)),
					Number(item.get(Parameter.TI_ITEM_PRICE)),
					parseInt(item.get(Parameter.TI_ITEM_QUANTITY)),
					String(item.get(Parameter.TI_ITEM_NAME)),
					String(item.get(Parameter.TI_ITEM_CATEGORY)),
					String(item.get(Parameter.TI_ITEM_CURRENCY)),
					item.get(Parameter.CONTEXT),
					timestamp);
			}

			addTrackerPayload(payload);
		}

		/**
		 * @param name The name of the screen view event
		 * @param id Screen view ID
		 * @param context Custom context for the event
		 * @param timestamp Optional user-provided timestamp for the event
		 */
		public function trackScreenView(name:String, id:String, context:Array,
			timestamp:Number):void {
				Preconditions.checkArgument(name != null || id != null);
				var trackerPayload:TrackerPayload = new TrackerPayload();

				trackerPayload.add(Parameter.SV_NAME, name);
				trackerPayload.add(Parameter.SV_ID, id);

				var payload:SchemaPayload = new SchemaPayload();
				payload.setSchema(Constants.SCHEMA_SCREEN_VIEW);
				payload.setData(trackerPayload);

				trackUnstructuredEvent(payload, context, timestamp);
			}
	}
}

/*
* Copyright (c) 2015 Snowplow Analytics Ltd. All rights reserved.
*
* This program is licensed to you under the Apache License Version 2.0,
* and you may not use this file except in compliance with the Apache License Version 2.0.
* You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the Apache License Version 2.0 is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
*/

package com.snowplowanalytics.snowplow.tracker
{
	import com.snowplowanalytics.snowplow.tracker.emitter.Emitter;
	import com.snowplowanalytics.snowplow.tracker.payload.IPayload;
	import com.snowplowanalytics.snowplow.tracker.payload.SchemaPayload;
	import com.snowplowanalytics.snowplow.tracker.payload.TrackerPayload;
	import com.snowplowanalytics.snowplow.tracker.util.Preconditions;

	import flash.display.Stage;
	import flash.external.ExternalInterface;
	import flash.net.LocalConnection;
	import flash.net.SharedObject;
	import flash.system.Capabilities;

	public class Tracker
	{
		private var base64Encoded:Boolean = true;
		private var emitter:Emitter;
		private var platform:String;
		private var appId:String;
		private var namespace:String;
		private var trackerVersion:String;
		private var subject:Subject;
		private var stage:Stage;

		private var playerType:String;
		private var playerVersion:String;
		private var isDebugger:Boolean;
		private var hasLocalStorage:Boolean;
		private var hasScriptAccess:Boolean;

		/**
		 * @param emitter Emitter to which events will be sent
		 * @param subject Subject to be tracked
		 * @param namespace Identifier for the Tracker instance
		 * @param appId Application ID
		 * @param stage The flash stage object.  used for adding stage info to payload.
		 * @param base64Encoded Whether JSONs in the payload should be base-64 encoded
		 */
		function Tracker(emitter:Emitter, subject:Subject, namespace:String, appId:String, stage:Stage = null, base64Encoded:Boolean = true) {
				this.emitter = emitter;
				this.appId = appId;
				this.base64Encoded = base64Encoded;
				this.namespace = namespace;
				this.subject = subject;
				this.trackerVersion = Version.TRACKER;
				this.platform = DevicePlatform.DESKTOP;
				this.stage = stage;

				this.playerType = Capabilities.playerType;
				this.playerVersion = Capabilities.version;
				this.isDebugger = Capabilities.isDebugger;

				try
				{
					SharedObject.getLocal("test");
					this.hasLocalStorage = true;
				}
				catch (e:Error)
				{
					this.hasLocalStorage = false;
				}
				this.hasScriptAccess = ExternalInterface.available;
			}

		/**
		 * @param payload Payload builder
		 * @param context Custom context for the event
		 * @param timestamp Optional user-provided timestamp for the event
		 * @return A completed Payload
		 */
		protected function completePayload(payload:IPayload,
										   context:Array,
										   timestamp:Number):IPayload {

				payload.add(Parameter.PLATFORM, this.platform);
				payload.add(Parameter.APPID, this.appId);
				payload.add(Parameter.NAMESPACE, this.namespace);
				payload.add(Parameter.TRACKER_VERSION, this.trackerVersion);
				payload.add(Parameter.EID, Util.getEventId());

				// If timestamp is set to 0, generate one
				payload.add(Parameter.TIMESTAMP,
					(timestamp == 0 ? Util.getTimestamp() : String(timestamp)));

				// Add flash information
				if (context == null) {
					context = [];
				}

				var flashData:TrackerPayload = new TrackerPayload();
				flashData.add(Parameter.FLASH_PLAYER_TYPE, playerType);
				flashData.add(Parameter.FLASH_VERSION, playerVersion);
				flashData.add(Parameter.FLASH_IS_DEBUGGER, isDebugger);
				flashData.add(Parameter.FLASH_HAS_LOCAL_STORAGE, hasLocalStorage);
				flashData.add(Parameter.FLASH_HAS_SCRIPT_ACCESS, hasScriptAccess);
				if (stage != null) {
					flashData.add(Parameter.FLASH_STAGE_SIZE, { "width": stage.stageWidth, "height": stage.stageHeight});
				}

				var flashPayload:SchemaPayload = new SchemaPayload();
				flashPayload.setSchema(Constants.SCHEMA_FLASH);
				flashPayload.setData(flashData.getMap());

				context.push(flashPayload);

				// Encodes context data
				if (context != null && context.length > 0) {
					var envelope:SchemaPayload = new SchemaPayload();
					envelope.setSchema(Constants.SCHEMA_CONTEXTS);

					// We can do better here, rather than re-iterate through the list
					var contextDataList:Array = [];
					for each(var schemaPayload:SchemaPayload in context) {
						contextDataList.push(schemaPayload.getMap());
					}

					envelope.setData(contextDataList);
					payload.addMap(envelope.getMap(), this.base64Encoded, Parameter.CONTEXT_ENCODED, Parameter.CONTEXT);
				}

				if (this.subject != null) {
					payload.addMap(Util.copyObject(subject.getSubject(), true));
				}

				return payload;
			}

		public function setPlatform(platform:String):void {
			this.platform = platform;
		}

		public function getPlatform():String {
			return this.platform;
		}

		protected function setTrackerVersion(version:String):void {
			this.trackerVersion = version;
		}

		private function addTrackerPayload(payload:IPayload):void {
			this.emitter.addToBuffer(payload);
		}

		public function setSubject(subject:Subject):void {
			this.subject = subject;
		}

		public function getSubject():Subject {
			return this.subject;
		}

		/**
		 * @param pageUrl URL of the viewed page
		 * @param pageTitle Title of the viewed page
		 * @param referrer Referrer of the page
		 * @param context Custom context for the event
		 * @param timestamp Optional user-provided timestamp for the event
		 */
		public function trackPageView(pageUrl:String, pageTitle:String, referrer:String, context:Array = null, timestamp:Number = 0):void
		{
			// Precondition checks
			Preconditions.checkNotNull(pageUrl);
			Preconditions.checkArgument(!Util.isNullOrEmpty(pageUrl), "pageUrl cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(pageTitle), "pageTitle cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(referrer), "referrer cannot be empty");

			var payload:IPayload = new TrackerPayload();
			payload.add(Parameter.EVENT, Constants.EVENT_PAGE_VIEW);
			payload.add(Parameter.PAGE_URL, pageUrl);
			payload.add(Parameter.PAGE_TITLE, pageTitle);
			payload.add(Parameter.PAGE_REFR, referrer);

			completePayload(payload, context, timestamp);

			addTrackerPayload(payload);
		}

		/**
		 * @param category Category of the event
		 * @param action The event itself
		 * @param label Refer to the object the action is performed on
		 * @param property Property associated with either the action or the object
		 * @param value A value associated with the user action
		 * @param context Custom context for the event
		 * @param timestamp Optional user-provided timestamp for the event
		 */
		public function trackStructuredEvent(category:String,
			action:String,
			label:String,
			property:String,
			value:int,
			context:Array = null,
			timestamp:Number = 0):void
		{
			// Precondition checks
			Preconditions.checkNotNull(label);
			Preconditions.checkNotNull(property);
			Preconditions.checkArgument(!Util.isNullOrEmpty(label), "label cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(property), "property cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(category), "category cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(action), "action cannot be empty");

			var payload:IPayload = new TrackerPayload();
			payload.add(Parameter.EVENT, Constants.EVENT_STRUCTURED);
			payload.add(Parameter.SE_CATEGORY, category);
			payload.add(Parameter.SE_ACTION, action);
			payload.add(Parameter.SE_LABEL, label);
			payload.add(Parameter.SE_PROPERTY, property);
			payload.add(Parameter.SE_VALUE, String(value));

			completePayload(payload, context, timestamp);

			addTrackerPayload(payload);
		}

		/**
		 *
		 * @param eventData The properties of the event. Has two field:
		 *                   A "data" field containing the event properties and
		 *                   A "schema" field identifying the schema against which the data is validated
		 * @param context Custom context for the event
		 * @param timestamp Optional user-provided timestamp for the event
		 */
		public function trackUnstructuredEvent(eventData:SchemaPayload, context:Array = null,
			timestamp:Number = 0):void
		{
			var envelope:SchemaPayload = new SchemaPayload();
			envelope.setSchema(Constants.SCHEMA_UNSTRUCT_EVENT);
			envelope.setData(eventData.getMap());

			var payload:IPayload = new TrackerPayload();
			payload.add(Parameter.EVENT, Constants.EVENT_UNSTRUCTURED);
			payload.addMap(envelope.getMap(), base64Encoded,
				Parameter.UNSTRUCTURED_ENCODED, Parameter.UNSTRUCTURED);

			completePayload(payload, context, timestamp);

			addTrackerPayload(payload);
		}

		/**
		 * This is an internal method called by track_ecommerce_transaction. It is not for public use.
		 * @param order_id Order ID
		 * @param sku Item SKU
		 * @param price Item price
		 * @param quantity Item quantity
		 * @param name Item name
		 * @param category Item category
		 * @param currency The currency the price is expressed in
		 * @param context Custom context for the event
		 * @param timestamp Optional user-provided timestamp for the event
		 */
		protected function trackEcommerceTransactionItem(order_id:String, sku:String, price:Number,
			quantity:int, name:String, category:String,
			currency:String, context:Array,
			timestamp:Number):void
		{
			// Precondition checks
			Preconditions.checkNotNull(name);
			Preconditions.checkNotNull(category);
			Preconditions.checkNotNull(currency);
			Preconditions.checkArgument(!Util.isNullOrEmpty(order_id), "order_id cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(sku), "sku cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(name), "name cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(category), "category cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(currency), "currency cannot be empty");

			var payload:IPayload = new TrackerPayload();
			payload.add(Parameter.EVENT, Constants.EVENT_ECOMM_ITEM);
			payload.add(Parameter.TI_ITEM_ID, order_id);
			payload.add(Parameter.TI_ITEM_SKU, sku);
			payload.add(Parameter.TI_ITEM_NAME, name);
			payload.add(Parameter.TI_ITEM_CATEGORY, category);
			payload.add(Parameter.TI_ITEM_PRICE, String(price));
			payload.add(Parameter.TI_ITEM_QUANTITY, String(quantity));
			payload.add(Parameter.TI_ITEM_CURRENCY, currency);

			completePayload(payload, context, timestamp);

			addTrackerPayload(payload);
		}

		/**
		 * @param order_id ID of the eCommerce transaction
		 * @param total_value Total transaction value
		 * @param affiliation Transaction affiliation
		 * @param tax_value Transaction tax value
		 * @param shipping Delivery cost charged
		 * @param city Delivery address city
		 * @param state Delivery address state
		 * @param country Delivery address country
		 * @param currency The currency the price is expressed in
		 * @param items The items in the transaction
		 * @param context Custom context for the event
		 * @param timestamp Optional user-provided timestamp for the event
		 */
		public function trackEcommerceTransaction(order_id:String, total_value:Number, affiliation:String,
			tax_value:Number, shipping:Number, city:String,
			state:String, country:String, currency:String,
			items:Array, context:Array = null,
			timestamp:Number = 0):void
		{
			// Precondition checks
			Preconditions.checkNotNull(affiliation);
			Preconditions.checkNotNull(city);
			Preconditions.checkNotNull(state);
			Preconditions.checkNotNull(country);
			Preconditions.checkNotNull(currency);
			Preconditions.checkArgument(!Util.isNullOrEmpty(order_id), "order_id cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(affiliation), "affiliation cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(city), "city cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(state), "state cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(country), "country cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(currency), "currency cannot be empty");

			var payload:IPayload = new TrackerPayload();
			payload.add(Parameter.EVENT, Constants.EVENT_ECOMM);
			payload.add(Parameter.TR_ID, order_id);
			payload.add(Parameter.TR_TOTAL, String(total_value));
			payload.add(Parameter.TR_AFFILIATION, affiliation);
			payload.add(Parameter.TR_TAX, String(tax_value));
			payload.add(Parameter.TR_SHIPPING, String(shipping));
			payload.add(Parameter.TR_CITY, city);
			payload.add(Parameter.TR_STATE, state);
			payload.add(Parameter.TR_COUNTRY, country);
			payload.add(Parameter.TR_CURRENCY, currency);

			completePayload(payload, context, timestamp);

			for each(var item:TransactionItem in items) {
				trackEcommerceTransactionItem(
					String(item.get(Parameter.TI_ITEM_ID)),
					String(item.get(Parameter.TI_ITEM_SKU)),
					Number(item.get(Parameter.TI_ITEM_PRICE)),
					parseInt(item.get(Parameter.TI_ITEM_QUANTITY)),
					String(item.get(Parameter.TI_ITEM_NAME)),
					String(item.get(Parameter.TI_ITEM_CATEGORY)),
					String(item.get(Parameter.TI_ITEM_CURRENCY)),
					item.get(Parameter.CONTEXT),
					timestamp);
			}

			addTrackerPayload(payload);
		}

		/**
		 * @param name The name of the screen view event
		 * @param id Screen view ID
		 * @param context Custom context for the event
		 * @param timestamp Optional user-provided timestamp for the event
		 */
		public function trackScreenView(name:String, id:String, context:Array,
			timestamp:Number):void {
				Preconditions.checkArgument(name != null || id != null);
				var trackerPayload:TrackerPayload = new TrackerPayload();

				trackerPayload.add(Parameter.SV_NAME, name);
				trackerPayload.add(Parameter.SV_ID, id);

				var payload:SchemaPayload = new SchemaPayload();
				payload.setSchema(Constants.SCHEMA_SCREEN_VIEW);
				payload.setData(trackerPayload);

				trackUnstructuredEvent(payload, context, timestamp);
			}
	}
}

fix trackUnstructuredEvent to place unstructured data into serialized json instead of on the payload itself.


package aerys.minko.scene.node.light
{
	import aerys.minko.ns.minko_scene;
	import aerys.minko.render.material.phong.PhongProperties;
	import aerys.minko.scene.controller.light.DirectionalLightController;
	import aerys.minko.scene.node.AbstractSceneNode;
	import aerys.minko.type.enum.ShadowMappingType;
	import aerys.minko.type.math.Vector4;

	use namespace minko_scene;

	/**
	 *
	 * @author Romain Gilliotte
	 * @author Jean-Marc Le Roux
	 *
	 */
	public class DirectionalLight extends AbstractLight
	{
		public static const LIGHT_TYPE : uint = 1;

		public function get diffuse() : Number
		{
			return lightData.getLightProperty('diffuse') as Number;
		}
		public function set diffuse(v : Number)	: void
		{
			lightData.setLightProperty('diffuse', v);

			if (lightData.getLightProperty('diffuseEnabled') != (v != 0))
				lightData.setLightProperty('diffuseEnabled', v != 0);
		}

		public function get specular() : Number
		{
			return lightData.getLightProperty('specular') as Number;
		}
		public function set specular(v : Number) : void
		{
			lightData.setLightProperty('specular', v);

			if (lightData.getLightProperty('specularEnabled') != (v != 0))
				lightData.setLightProperty('specularEnabled', v != 0);
		}

		public function get shininess() : Number
		{
			return lightData.getLightProperty('shininess') as Number;
		}
		public function set shininess(value : Number) : void
		{
			lightData.setLightProperty('shininess', value);
		}

		public function get shadowWidth() : Number
		{
			return lightData.getLightProperty('shadowWidth');
		}
		public function set shadowWidth(v : Number) : void
		{
			lightData.setLightProperty('shadowWidth', v);
		}

		public function get shadowZNear() : Number
		{
			return lightData.getLightProperty('shadowZNear');
		}
		public function set shadowZNear(v : Number) : void
		{
			lightData.setLightProperty('shadowZNear', v);
		}

		public function get shadowZFar() : Number
		{
			return lightData.getLightProperty('shadowZFar');
		}
		public function set shadowZFar(v : Number) : void
		{
			lightData.setLightProperty('shadowZFar', v);
		}

		public function get shadowQuality() : uint
		{
			return lightData.getLightProperty('shadowQuality');
		}
		public function set shadowQuality(v : uint) : void
		{
			lightData.setLightProperty('shadowQuality', v);
		}

		public function get shadowSpread() : uint
		{
			return lightData.getLightProperty('shadowSpread');
		}
		public function set shadowSpread(v : uint) : void
		{
			lightData.setLightProperty('shadowSpread', v);
		}

		public function get shadowMapSize() : uint
		{
			return lightData.getLightProperty('shadowMapSize');
		}
		public function set shadowMapSize(v : uint) : void
		{
			lightData.setLightProperty('shadowMapSize', v);
		}

		public function get shadowMappingType() : uint
		{
			return lightData.getLightProperty('shadowMappingType');
		}
		public function set shadowMappingType(value : uint) : void
		{
			lightData.setLightProperty('shadowMappingType', value);
		}

        public function get shadowBias() : Number
        {
            return lightData.getLightProperty(PhongProperties.SHADOW_BIAS);
        }
        public function set shadowBias(value : Number) : void
        {
            lightData.setLightProperty(PhongProperties.SHADOW_BIAS, value);
        }

		public function DirectionalLight(color				: uint		= 0xFFFFFFFF,
									 	 diffuse			: Number	= .6,
										 specular			: Number	= .8,
										 shininess			: Number	= 64,
										 emissionMask		: uint		= 0x1,
										 shadowMappingType	: uint		= 0,
										 shadowMapSize		: uint		= 512,
										 shadowZFar			: Number	= 1000,
										 shadowWidth		: Number	= 20,
										 shadowQuality		: uint		= 0,
										 shadowSpread		: uint		= 1,
                                         shadowBias         : Number    = .002,
										 shadowColor		: uint		= 0x0)
		{
			super(
				new DirectionalLightController(),
				LIGHT_TYPE,
				color,
				emissionMask
			);

			this.diffuse			= diffuse;
			this.specular			= specular;
			this.shininess			= shininess;
			this.shadowMappingType	= shadowMappingType;
			this.shadowZFar			= shadowZFar;
			this.shadowWidth		= shadowWidth;
			this.shadowMapSize		= shadowMapSize;
			this.shadowQuality		= shadowQuality;
			this.shadowSpread		= shadowSpread;
            this.shadowBias     	= shadowBias;
			this.shadowColor		= shadowColor;

			transform.lookAt(Vector4.ZERO, new Vector4(1, 1, 1));
		}

		override minko_scene function cloneNode() : AbstractSceneNode
		{
			var light : DirectionalLight = new DirectionalLight(
				color,
				diffuse,
				specular,
				shininess,
				emissionMask,
				ShadowMappingType.NONE, // @fixme clone light with shadow triggered errors
				shadowMapSize,
				shadowZFar,
				shadowWidth,
				shadowQuality,
				shadowSpread,
                shadowBias
			);

			light.name = this.name;
			light.userData.setProperties(userData);
			light.transform.copyFrom(this.transform);

			return light;
		}
	}
}

package aerys.minko.scene.node.light
{
	import aerys.minko.ns.minko_scene;
	import aerys.minko.render.material.phong.PhongProperties;
	import aerys.minko.scene.controller.light.DirectionalLightController;
	import aerys.minko.scene.node.AbstractSceneNode;
	import aerys.minko.type.enum.ShadowMappingType;
	import aerys.minko.type.math.Vector4;

	use namespace minko_scene;

	/**
	 *
	 * @author Romain Gilliotte
	 * @author Jean-Marc Le Roux
	 *
	 */
	public class DirectionalLight extends AbstractLight
	{
		public static const LIGHT_TYPE : uint = 1;

		public function get diffuse() : Number
		{
			return lightData.getLightProperty('diffuse') as Number;
		}
		public function set diffuse(v : Number)	: void
		{
			lightData.setLightProperty('diffuse', v);

			if (lightData.getLightProperty('diffuseEnabled') != (v != 0))
				lightData.setLightProperty('diffuseEnabled', v != 0);
		}

		public function get specular() : Number
		{
			return lightData.getLightProperty('specular') as Number;
		}
		public function set specular(v : Number) : void
		{
			lightData.setLightProperty('specular', v);

			if (lightData.getLightProperty('specularEnabled') != (v != 0))
				lightData.setLightProperty('specularEnabled', v != 0);
		}

		public function get shininess() : Number
		{
			return lightData.getLightProperty('shininess') as Number;
		}
		public function set shininess(value : Number) : void
		{
			lightData.setLightProperty('shininess', value);
		}

		public function get shadowWidth() : Number
		{
			return lightData.getLightProperty('shadowWidth');
		}
		public function set shadowWidth(v : Number) : void
		{
			lightData.setLightProperty('shadowWidth', v);
		}

		public function get shadowZNear() : Number
		{
			return lightData.getLightProperty('shadowZNear');
		}
		public function set shadowZNear(v : Number) : void
		{
			lightData.setLightProperty('shadowZNear', v);
		}

		public function get shadowZFar() : Number
		{
			return lightData.getLightProperty('shadowZFar');
		}
		public function set shadowZFar(v : Number) : void
		{
			lightData.setLightProperty('shadowZFar', v);
		}

		public function get shadowQuality() : uint
		{
			return lightData.getLightProperty('shadowQuality');
		}
		public function set shadowQuality(v : uint) : void
		{
			lightData.setLightProperty('shadowQuality', v);
		}

		public function get shadowSpread() : uint
		{
			return lightData.getLightProperty('shadowSpread');
		}
		public function set shadowSpread(v : uint) : void
		{
			lightData.setLightProperty('shadowSpread', v);
		}

		public function get shadowMapSize() : uint
		{
			return lightData.getLightProperty('shadowMapSize');
		}
		public function set shadowMapSize(v : uint) : void
		{
			lightData.setLightProperty('shadowMapSize', v);
		}

		public function get shadowMappingType() : uint
		{
			return lightData.getLightProperty('shadowMappingType');
		}
		public function set shadowMappingType(value : uint) : void
		{
			lightData.setLightProperty('shadowMappingType', value);
		}

        public function get shadowBias() : Number
        {
            return lightData.getLightProperty(PhongProperties.SHADOW_BIAS);
        }
        public function set shadowBias(value : Number) : void
        {
            lightData.setLightProperty(PhongProperties.SHADOW_BIAS, value);
        }

		public function DirectionalLight(color				: uint		= 0xFFFFFFFF,
									 	 diffuse			: Number	= .6,
										 specular			: Number	= .8,
										 shininess			: Number	= 64,
										 emissionMask		: uint		= 0x1,
										 shadowMappingType	: uint		= 0,
										 shadowMapSize		: uint		= 512,
										 shadowZFar			: Number	= 1000,
										 shadowWidth		: Number	= 20,
										 shadowQuality		: uint		= 0,
										 shadowSpread		: uint		= 1,
                                         shadowBias         : Number    = .002,
										 shadowColor		: uint		= 0x0)
		{
			super(
				new DirectionalLightController(),
				LIGHT_TYPE,
				color,
				emissionMask
			);

			this.diffuse			= diffuse;
			this.specular			= specular;
			this.shininess			= shininess;
			this.shadowMappingType	= shadowMappingType;
			this.shadowZFar			= shadowZFar;
			this.shadowWidth		= shadowWidth;
			this.shadowMapSize		= shadowMapSize;
			this.shadowQuality		= shadowQuality;
			this.shadowSpread		= shadowSpread;
            this.shadowBias     	= shadowBias;
			this.shadowColor		= shadowColor;
			this.shadowZNear 		= 0;

			transform.lookAt(Vector4.ZERO, new Vector4(1, 1, 1));
		}

		override minko_scene function cloneNode() : AbstractSceneNode
		{
			var light : DirectionalLight = new DirectionalLight(
				color,
				diffuse,
				specular,
				shininess,
				emissionMask,
				ShadowMappingType.NONE, // @fixme clone light with shadow triggered errors
				shadowMapSize,
				shadowZFar,
				shadowWidth,
				shadowQuality,
				shadowSpread,
                shadowBias
			);

			light.name = this.name;
			light.userData.setProperties(userData);
			light.transform.copyFrom(this.transform);

			return light;
		}
	}
}

Fix default znear value

Fix default znear value


////////////////////////////////////////////////////////////////////////////////
// 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.components.serviceBrowser.supportClasses
{

import com.esri.ags.components.IdentityManager;
import com.esri.ags.components.supportClasses.Credential;
import com.esri.ags.tasks.JSONTask;
import com.esri.builder.components.serviceBrowser.filters.GPTaskFilter;
import com.esri.builder.components.serviceBrowser.filters.GeocoderFilter;
import com.esri.builder.components.serviceBrowser.filters.INodeFilter;
import com.esri.builder.components.serviceBrowser.filters.MapLayerFilter;
import com.esri.builder.components.serviceBrowser.filters.MapServerFilter;
import com.esri.builder.components.serviceBrowser.filters.QueryableLayerFilter;
import com.esri.builder.components.serviceBrowser.filters.RouteLayerFilter;
import com.esri.builder.components.serviceBrowser.nodes.ServiceDirectoryRootNode;
import com.esri.builder.model.Model;
import com.esri.builder.supportClasses.LogUtil;

import flash.events.EventDispatcher;
import flash.net.URLVariables;

import mx.logging.ILogger;
import mx.logging.Log;
import mx.resources.ResourceManager;
import mx.rpc.AsyncResponder;
import mx.rpc.Fault;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.rpc.http.HTTPService;

public final class ServiceDirectoryBuilder extends EventDispatcher
{
    private static const LOG:ILogger = LogUtil.createLogger(ServiceDirectoryBuilder);

    private static const DEFAULT_REQUEST_TIMEOUT_IN_SECONDS:Number = 10;

    private var count:int;
    private var searchType:String;

    private var hasCrossDomain:Boolean;
    private var crossDomainRequest:HTTPService;
    private var credential:Credential;
    private var rootNode:ServiceDirectoryRootNode;
    private var currentNodeFilter:INodeFilter;
    private var isServiceSecured:Boolean;
    private var securityWarning:String;
    private var owningSystemURL:String;

    public function buildServiceDirectory(serviceDirectoryBuildRequest:ServiceDirectoryBuildRequest):void
    {
        if (Log.isInfo())
        {
            LOG.info("Building service directory");
        }

        try
        {
            checkCrossDomainBeforeBuildingDirectory(serviceDirectoryBuildRequest);
        }
        catch (error:Error)
        {
            //TODO: handle error
        }
    }

    private function checkCrossDomainBeforeBuildingDirectory(serviceDirectoryBuildRequest:ServiceDirectoryBuildRequest):void
    {
        if (Log.isDebug())
        {
            LOG.debug("Checking service URL crossdomain.xml");
        }

        crossDomainRequest = new HTTPService();
        crossDomainRequest.url = extractCrossDomainPolicyFileURL(serviceDirectoryBuildRequest.url);
        crossDomainRequest.addEventListener(ResultEvent.RESULT, crossDomainRequest_resultHandler);
        crossDomainRequest.addEventListener(FaultEvent.FAULT, crossDomainRequest_faultHandler);
        crossDomainRequest.requestTimeout = DEFAULT_REQUEST_TIMEOUT_IN_SECONDS;
        crossDomainRequest.send();

        function crossDomainRequest_resultHandler(event:ResultEvent):void
        {
            if (Log.isDebug())
            {
                LOG.debug("Found service crossdomain.xml");
            }

            crossDomainRequest.removeEventListener(ResultEvent.RESULT, crossDomainRequest_resultHandler);
            crossDomainRequest.removeEventListener(FaultEvent.FAULT, crossDomainRequest_faultHandler);
            crossDomainRequest = null;
            hasCrossDomain = true;

            checkIfServiceIsSecure(serviceDirectoryBuildRequest);
        }

        function crossDomainRequest_faultHandler(event:FaultEvent):void
        {
            if (Log.isDebug())
            {
                LOG.debug("Could not find service crossdomain.xml");
            }

            crossDomainRequest.removeEventListener(ResultEvent.RESULT, crossDomainRequest_resultHandler);
            crossDomainRequest.removeEventListener(FaultEvent.FAULT, crossDomainRequest_faultHandler);
            crossDomainRequest = null;
            hasCrossDomain = false;

            checkIfServiceIsSecure(serviceDirectoryBuildRequest);
        }
    }

    private function extractCrossDomainPolicyFileURL(url:String):String
    {
        var baseURL:RegExp = /(https?:\/\/)([^\/]+\/)/
        var baseURLMatch:Array = url.match(baseURL);
        return baseURLMatch[0] + 'crossdomain.xml';
    }

    private function checkIfServiceIsSecure(serviceDirectoryBuildRequest:ServiceDirectoryBuildRequest):void
    {
        if (Log.isInfo())
        {
            LOG.info("Checking if service is secure");
        }

        var serviceSecurityRequest:JSONTask = new JSONTask();
        serviceSecurityRequest.url = extractServiceInfoURL(serviceDirectoryBuildRequest.url);
        const param:URLVariables = new URLVariables();
        param.f = 'json';
        serviceSecurityRequest.execute(param, new AsyncResponder(serviceSecurityRequest_resultHandler, serviceSecurityRequest_faultHandler));

        function serviceSecurityRequest_resultHandler(serverInfo:Object, token:Object = null):void
        {
            isServiceSecured = (serverInfo.currentVersion >= 10.01
                && serverInfo.authInfo
                && serverInfo.authInfo.isTokenBasedSecurity);

            owningSystemURL = serverInfo.owningSystemUrl;

            if (isServiceSecured)
            {
                if (Log.isDebug())
                {
                    LOG.debug("Service is secure");
                }

                if (Log.isDebug())
                {
                    LOG.debug("Checking token service crossdomain.xml");
                }

                const tokenServiceCrossDomainRequest:HTTPService = new HTTPService();
                tokenServiceCrossDomainRequest.url = extractCrossDomainPolicyFileURL(serverInfo.authInfo.tokenServicesUrl);
                tokenServiceCrossDomainRequest.resultFormat = HTTPService.RESULT_FORMAT_E4X;
                tokenServiceCrossDomainRequest.addEventListener(ResultEvent.RESULT, tokenServiceSecurityRequest_resultHandler);
                tokenServiceCrossDomainRequest.addEventListener(FaultEvent.FAULT, tokenServiceSecurityRequest_faultHandler);
                tokenServiceCrossDomainRequest.send();

                function tokenServiceSecurityRequest_resultHandler(event:ResultEvent):void
                {
                    if (Log.isDebug())
                    {
                        LOG.debug("Found token service crossdomain.xml");
                    }

                    tokenServiceCrossDomainRequest.removeEventListener(ResultEvent.RESULT, tokenServiceSecurityRequest_resultHandler);
                    tokenServiceCrossDomainRequest.removeEventListener(FaultEvent.FAULT, tokenServiceSecurityRequest_faultHandler);

                    const startsWithHTTPS:RegExp = /^https/;
                    if (startsWithHTTPS.test(tokenServiceCrossDomainRequest.url))
                    {
                        const tokenServiceCrossDomainXML:XML = event.result as XML;
                        const hasSecurityEnabled:Boolean =
                            tokenServiceCrossDomainXML.child("allow-access-from").(attribute("secure") == "false").length() == 0
                            || tokenServiceCrossDomainXML.child("allow-http-request-headers-from").(attribute("secure") == "false").length() == 0;

                        if (hasSecurityEnabled
                            && !startsWithHTTPS.test(Model.instance.appDir.url))
                        {
                            securityWarning = ResourceManager.getInstance().getString('BuilderStrings', 'serviceBrowser.secureTokenServiceWithSecureCrossDomain');
                        }
                    }
                }

                function tokenServiceSecurityRequest_faultHandler(event:FaultEvent):void
                {
                    if (Log.isDebug())
                    {
                        LOG.debug("Could not find token service crossdomain.xml");
                    }

                    tokenServiceCrossDomainRequest.removeEventListener(ResultEvent.RESULT, tokenServiceSecurityRequest_resultHandler);
                    tokenServiceCrossDomainRequest.removeEventListener(FaultEvent.FAULT, tokenServiceSecurityRequest_faultHandler);
                    securityWarning = ResourceManager.getInstance().getString('BuilderStrings', 'serviceBrowser.secureTokenServiceMissingCrossDomain');
                }
            }

            //continue with building service directory
            startBuildingDirectory(serviceDirectoryBuildRequest);
        }

        function serviceSecurityRequest_faultHandler(fault:Fault, token:Object = null):void
        {
            //continue with building service directory
            startBuildingDirectory(serviceDirectoryBuildRequest);
        }
    }

    private function extractServiceInfoURL(url:String):String
    {
        return url.replace('/rest/services', '/rest/info');
    }

    private function startBuildingDirectory(serviceDirectoryBuildRequest:ServiceDirectoryBuildRequest):void
    {
        if (Log.isInfo())
        {
            LOG.info('Building serviced directory {0}', serviceDirectoryBuildRequest.url);
        }

        const servicesDirectoryURL:RegExp = /.+\/rest\/services\/?/;
        const serviceURLRootMatch:Array = servicesDirectoryURL.exec(serviceDirectoryBuildRequest.url);

        if (!serviceURLRootMatch)
        {
            return;
        }

        currentNodeFilter = filterFromSearchType(serviceDirectoryBuildRequest.searchType);
        rootNode = new ServiceDirectoryRootNode(serviceDirectoryBuildRequest.url, currentNodeFilter);
        rootNode.addEventListener(URLNodeTraversalEvent.END_REACHED, rootNode_completeHandler);
        rootNode.addEventListener(FaultEvent.FAULT, rootNode_faultHandler);
        credential = IdentityManager.instance.findCredential(serviceURLRootMatch[0]);
        if (credential)
        {
            rootNode.token = credential.token;
        }
        rootNode.loadChildren();
    }

    private function filterFromSearchType(searchType:String):INodeFilter
    {
        if (searchType == ServiceDirectoryBuildRequest.QUERYABLE_LAYER_SEARCH)
        {
            return new QueryableLayerFilter();
        }
        else if (searchType == ServiceDirectoryBuildRequest.GEOPROCESSING_TASK_SEARCH)
        {
            return new GPTaskFilter();
        }
        else if (searchType == ServiceDirectoryBuildRequest.GEOCODER_SEARCH)
        {
            return new GeocoderFilter();
        }
        else if (searchType == ServiceDirectoryBuildRequest.ROUTE_LAYER_SEARCH)
        {
            return new RouteLayerFilter();
        }
        else if (searchType == ServiceDirectoryBuildRequest.MAP_SERVER_SEARCH)
        {
            return new MapServerFilter();
        }
        else //MAP LAYER SEARCH
        {
            return new MapLayerFilter();
        }
    }

    private function rootNode_completeHandler(event:URLNodeTraversalEvent):void
    {
        if (Log.isInfo())
        {
            LOG.info("Finished building service directory");
        }

        dispatchEvent(
            new ServiceDirectoryBuilderEvent(ServiceDirectoryBuilderEvent.COMPLETE,
                                             new ServiceDirectoryInfo(rootNode,
                                                                      event.urlNodes,
                                                                      currentNodeFilter,
                                                                      hasCrossDomain,
                                                                      isServiceSecured,
                                                                      securityWarning,
                                                                      owningSystemURL)));
    }

    protected function rootNode_faultHandler(event:FaultEvent):void
    {
        if (Log.isInfo())
        {
            LOG.info("Could not build service directory");
        }

        dispatchEvent(event);
    }
}
}

////////////////////////////////////////////////////////////////////////////////
// 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.components.serviceBrowser.supportClasses
{

import com.esri.ags.components.IdentityManager;
import com.esri.ags.components.supportClasses.Credential;
import com.esri.ags.tasks.JSONTask;
import com.esri.builder.components.serviceBrowser.filters.GPTaskFilter;
import com.esri.builder.components.serviceBrowser.filters.GeocoderFilter;
import com.esri.builder.components.serviceBrowser.filters.INodeFilter;
import com.esri.builder.components.serviceBrowser.filters.MapLayerFilter;
import com.esri.builder.components.serviceBrowser.filters.MapServerFilter;
import com.esri.builder.components.serviceBrowser.filters.QueryableLayerFilter;
import com.esri.builder.components.serviceBrowser.filters.RouteLayerFilter;
import com.esri.builder.components.serviceBrowser.nodes.ServiceDirectoryRootNode;
import com.esri.builder.model.Model;
import com.esri.builder.supportClasses.LogUtil;

import flash.events.EventDispatcher;
import flash.net.URLVariables;

import mx.logging.ILogger;
import mx.logging.Log;
import mx.resources.ResourceManager;
import mx.rpc.AsyncResponder;
import mx.rpc.Fault;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.rpc.http.HTTPService;

public final class ServiceDirectoryBuilder extends EventDispatcher
{
    private static const LOG:ILogger = LogUtil.createLogger(ServiceDirectoryBuilder);

    private static const DEFAULT_REQUEST_TIMEOUT_IN_SECONDS:Number = 10;

    private var count:int;
    private var searchType:String;

    private var serviceDirectoryBuildRequest:ServiceDirectoryBuildRequest;
    private var hasCrossDomain:Boolean;
    private var crossDomainRequest:HTTPService;
    private var credential:Credential;
    private var rootNode:ServiceDirectoryRootNode;
    private var currentNodeFilter:INodeFilter;
    private var isServiceSecured:Boolean;
    private var securityWarning:String;
    private var owningSystemURL:String;

    public function buildServiceDirectory(serviceDirectoryBuildRequest:ServiceDirectoryBuildRequest):void
    {
        if (Log.isInfo())
        {
            LOG.info("Building service directory");
        }

        this.serviceDirectoryBuildRequest = serviceDirectoryBuildRequest;

        try
        {
            checkCrossDomainBeforeBuildingDirectory();
        }
        catch (error:Error)
        {
            //TODO: handle error
        }
    }

    private function checkCrossDomainBeforeBuildingDirectory():void
    {
        if (Log.isDebug())
        {
            LOG.debug("Checking service URL crossdomain.xml");
        }

        crossDomainRequest = new HTTPService();
        crossDomainRequest.url = extractCrossDomainPolicyFileURL(serviceDirectoryBuildRequest.url);
        crossDomainRequest.addEventListener(ResultEvent.RESULT, crossDomainRequest_resultHandler);
        crossDomainRequest.addEventListener(FaultEvent.FAULT, crossDomainRequest_faultHandler);
        crossDomainRequest.requestTimeout = DEFAULT_REQUEST_TIMEOUT_IN_SECONDS;
        crossDomainRequest.send();

        function crossDomainRequest_resultHandler(event:ResultEvent):void
        {
            if (Log.isDebug())
            {
                LOG.debug("Found service crossdomain.xml");
            }

            crossDomainRequest.removeEventListener(ResultEvent.RESULT, crossDomainRequest_resultHandler);
            crossDomainRequest.removeEventListener(FaultEvent.FAULT, crossDomainRequest_faultHandler);
            crossDomainRequest = null;
            hasCrossDomain = true;

            checkIfServiceIsSecure();
        }

        function crossDomainRequest_faultHandler(event:FaultEvent):void
        {
            if (Log.isDebug())
            {
                LOG.debug("Could not find service crossdomain.xml");
            }

            crossDomainRequest.removeEventListener(ResultEvent.RESULT, crossDomainRequest_resultHandler);
            crossDomainRequest.removeEventListener(FaultEvent.FAULT, crossDomainRequest_faultHandler);
            crossDomainRequest = null;
            hasCrossDomain = false;

            checkIfServiceIsSecure();
        }
    }

    private function extractCrossDomainPolicyFileURL(url:String):String
    {
        var baseURL:RegExp = /(https?:\/\/)([^\/]+\/)/
        var baseURLMatch:Array = url.match(baseURL);
        return baseURLMatch[0] + 'crossdomain.xml';
    }

    private function checkIfServiceIsSecure():void
    {
        if (Log.isInfo())
        {
            LOG.info("Checking if service is secure");
        }

        var serviceSecurityRequest:JSONTask = new JSONTask();
        serviceSecurityRequest.url = extractServiceInfoURL(serviceDirectoryBuildRequest.url);
        const param:URLVariables = new URLVariables();
        param.f = 'json';
        serviceSecurityRequest.execute(param, new AsyncResponder(serviceSecurityRequest_resultHandler, serviceSecurityRequest_faultHandler));

        function serviceSecurityRequest_resultHandler(serverInfo:Object, token:Object = null):void
        {
            isServiceSecured = (serverInfo.currentVersion >= 10.01
                && serverInfo.authInfo
                && serverInfo.authInfo.isTokenBasedSecurity);

            owningSystemURL = serverInfo.owningSystemUrl;

            if (isServiceSecured)
            {
                if (Log.isDebug())
                {
                    LOG.debug("Service is secure");
                }

                if (Log.isDebug())
                {
                    LOG.debug("Checking token service crossdomain.xml");
                }

                const tokenServiceCrossDomainRequest:HTTPService = new HTTPService();
                tokenServiceCrossDomainRequest.url = extractCrossDomainPolicyFileURL(serverInfo.authInfo.tokenServicesUrl);
                tokenServiceCrossDomainRequest.resultFormat = HTTPService.RESULT_FORMAT_E4X;
                tokenServiceCrossDomainRequest.addEventListener(ResultEvent.RESULT, tokenServiceSecurityRequest_resultHandler);
                tokenServiceCrossDomainRequest.addEventListener(FaultEvent.FAULT, tokenServiceSecurityRequest_faultHandler);
                tokenServiceCrossDomainRequest.send();

                function tokenServiceSecurityRequest_resultHandler(event:ResultEvent):void
                {
                    if (Log.isDebug())
                    {
                        LOG.debug("Found token service crossdomain.xml");
                    }

                    tokenServiceCrossDomainRequest.removeEventListener(ResultEvent.RESULT, tokenServiceSecurityRequest_resultHandler);
                    tokenServiceCrossDomainRequest.removeEventListener(FaultEvent.FAULT, tokenServiceSecurityRequest_faultHandler);

                    const startsWithHTTPS:RegExp = /^https/;
                    if (startsWithHTTPS.test(tokenServiceCrossDomainRequest.url))
                    {
                        const tokenServiceCrossDomainXML:XML = event.result as XML;
                        const hasSecurityEnabled:Boolean =
                            tokenServiceCrossDomainXML.child("allow-access-from").(attribute("secure") == "false").length() == 0
                            || tokenServiceCrossDomainXML.child("allow-http-request-headers-from").(attribute("secure") == "false").length() == 0;

                        if (hasSecurityEnabled
                            && !startsWithHTTPS.test(Model.instance.appDir.url))
                        {
                            securityWarning = ResourceManager.getInstance().getString('BuilderStrings', 'serviceBrowser.secureTokenServiceWithSecureCrossDomain');
                        }
                    }
                }

                function tokenServiceSecurityRequest_faultHandler(event:FaultEvent):void
                {
                    if (Log.isDebug())
                    {
                        LOG.debug("Could not find token service crossdomain.xml");
                    }

                    tokenServiceCrossDomainRequest.removeEventListener(ResultEvent.RESULT, tokenServiceSecurityRequest_resultHandler);
                    tokenServiceCrossDomainRequest.removeEventListener(FaultEvent.FAULT, tokenServiceSecurityRequest_faultHandler);
                    securityWarning = ResourceManager.getInstance().getString('BuilderStrings', 'serviceBrowser.secureTokenServiceMissingCrossDomain');
                }
            }

            //continue with building service directory
            startBuildingDirectory();
        }

        function serviceSecurityRequest_faultHandler(fault:Fault, token:Object = null):void
        {
            //continue with building service directory
            startBuildingDirectory();
        }
    }

    private function extractServiceInfoURL(url:String):String
    {
        return url.replace('/rest/services', '/rest/info');
    }

    private function startBuildingDirectory():void
    {
        if (Log.isInfo())
        {
            LOG.info('Building serviced directory {0}', serviceDirectoryBuildRequest.url);
        }

        const servicesDirectoryURL:RegExp = /.+\/rest\/services\/?/;
        const serviceURLRootMatch:Array = servicesDirectoryURL.exec(serviceDirectoryBuildRequest.url);

        if (!serviceURLRootMatch)
        {
            return;
        }

        currentNodeFilter = filterFromSearchType(serviceDirectoryBuildRequest.searchType);
        rootNode = new ServiceDirectoryRootNode(serviceDirectoryBuildRequest.url, currentNodeFilter);
        rootNode.addEventListener(URLNodeTraversalEvent.END_REACHED, rootNode_completeHandler);
        rootNode.addEventListener(FaultEvent.FAULT, rootNode_faultHandler);
        credential = IdentityManager.instance.findCredential(serviceURLRootMatch[0]);
        if (credential)
        {
            rootNode.token = credential.token;
        }
        rootNode.loadChildren();
    }

    private function filterFromSearchType(searchType:String):INodeFilter
    {
        if (searchType == ServiceDirectoryBuildRequest.QUERYABLE_LAYER_SEARCH)
        {
            return new QueryableLayerFilter();
        }
        else if (searchType == ServiceDirectoryBuildRequest.GEOPROCESSING_TASK_SEARCH)
        {
            return new GPTaskFilter();
        }
        else if (searchType == ServiceDirectoryBuildRequest.GEOCODER_SEARCH)
        {
            return new GeocoderFilter();
        }
        else if (searchType == ServiceDirectoryBuildRequest.ROUTE_LAYER_SEARCH)
        {
            return new RouteLayerFilter();
        }
        else if (searchType == ServiceDirectoryBuildRequest.MAP_SERVER_SEARCH)
        {
            return new MapServerFilter();
        }
        else //MAP LAYER SEARCH
        {
            return new MapLayerFilter();
        }
    }

    private function rootNode_completeHandler(event:URLNodeTraversalEvent):void
    {
        if (Log.isInfo())
        {
            LOG.info("Finished building service directory");
        }

        dispatchEvent(
            new ServiceDirectoryBuilderEvent(ServiceDirectoryBuilderEvent.COMPLETE,
                                             new ServiceDirectoryInfo(rootNode,
                                                                      event.urlNodes,
                                                                      currentNodeFilter,
                                                                      hasCrossDomain,
                                                                      isServiceSecured,
                                                                      securityWarning,
                                                                      owningSystemURL)));
    }

    protected function rootNode_faultHandler(event:FaultEvent):void
    {
        if (Log.isInfo())
        {
            LOG.info("Could not build service directory");
        }

        dispatchEvent(event);
    }
}
}

Add field for service directory build request (ServiceDirectoryBuilder).


//
// $Id$
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

package com.threerings.flash {

import flash.ui.ContextMenuItem;
import flash.events.ContextMenuEvent;

import com.threerings.util.CommandEvent;

/**
 */
public class MenuUtil
{
    /**
     * Create a menu item that will submit a controller command when selected.
     */
    public static function createControllerMenuItem (
            caption :String, cmd :String, arg :Object = null,
            separatorBefore :Boolean = false, enabled :Boolean = true,
            visible :Boolean = true) :ContextMenuItem
    {
        var item :ContextMenuItem =
            new ContextMenuItem(caption, separatorBefore, enabled, visible);
        item.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT,
            function (event :ContextMenuEvent) :void {
                CommandEvent.dispatch(event.mouseTarget, cmd, arg);
            });
        return item;
    }
}
}

//
// $Id$
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

package com.threerings.flash {

import flash.ui.ContextMenuItem;
import flash.events.ContextMenuEvent;

import com.threerings.util.CommandEvent;

/**
 */
public class MenuUtil
{
    /**
     * Create a menu item that will submit a controller command when selected.
     */
    public static function createControllerMenuItem (
            caption :String, cmdOrFn :Object, arg :Object = null,
            separatorBefore :Boolean = false, enabled :Boolean = true,
            visible :Boolean = true) :ContextMenuItem
    {
        var item :ContextMenuItem =
            new ContextMenuItem(caption, separatorBefore, enabled, visible);
        item.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT,
            function (event :ContextMenuEvent) :void {
                CommandEvent.dispatch(event.mouseTarget, cmdOrFn, arg);
            });
        return item;
    }
}
}

Allow a function to be specified here, too.

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