package aerys.minko.render.shader.part.phong.contribution
{
	import aerys.minko.render.material.basic.BasicProperties;
	import aerys.minko.render.material.phong.PhongProperties;
	import aerys.minko.render.shader.SFloat;
	import aerys.minko.render.shader.Shader;
	import aerys.minko.render.shader.part.phong.LightAwareShaderPart;
	import aerys.minko.type.enum.SamplerFiltering;
	import aerys.minko.type.enum.SamplerFormat;

	/**
	 * Methods in this class allow to compute light contributions.
	 * It is extended by InfiniteShaderPart for directional lights, and LocalizedShaderPart for point and spot lights.
	 *
	 * All methods are available in tangent, local and world space.
	 *
	 * - Computing in tangent space is computationally more expensive, both in CPU and GPU, because we need to feed
	 * more data to the shaders than the other methods but it allows to do both bump and parallax mapping.
	 *
	 * - Doing it in local space is computationally more expensive on the CPU, because the light
	 * position will need to be converted in the CPU for each mesh and at each frame will produce the lighter shaders.
	 *
	 * Doing as much work on the CPU as possible, and have as short a possible shader would be the way to go
	 * if adobe's compiler was better than it is. Therefor, minko defaults to computing everything in world space
	 * which is faster in practical tests.
	 *
	 * The cost of each light will be in O(number of drawcalls) on the CPU.
	 * However, this is the way that will lead to the smaller shaders, and is therefor recomended when used
	 * in small scenes with many lights.
	 *
	 * - Computing lights in world space is cheaper on the CPU, because no extra computation is
	 * needed for each mesh, but will cost one extra matrix multiplication on the GPU (we need the world position
	 * or every fragment).
	 *
	 * @author Romain Gilliotte
	 *
	 */
	public class AbstractContributionShaderPart extends LightAwareShaderPart
	{

		public function AbstractContributionShaderPart(main : Shader)
		{
			super(main);
		}

		/**
		 * Creates the shader subgraph to compute the diffuse value of a given light.
		 * Computations will be done in tangent space.
		 *
		 * @param lightId The id of the localized light (PointLight or SpotLight)
		 * @return Shader subgraph representing the diffuse value of this light.
		 */
		public function computeDiffuseInTangentSpace(lightId : uint) : SFloat
		{
			throw new Error('Must be overriden');
		}

		/**
		 * Creates the shader subgraph to compute the diffuse value of a given light.
		 * Computation will be done in local space.
		 *
		 * @param lightId The id of the localized light (PointLight or SpotLight)
		 * @return Shader subgraph representing the diffuse value of this light.
		 */
		public function computeDiffuseInLocalSpace(lightId : uint) : SFloat
		{
			throw new Error('Must be overriden');
		}

		/**
		 * Creates the shader subgraph to compute the diffuse value of a given light.
		 * Computation will be done in world space.
		 *
		 * @param lightId The id of the light
		 * @return Shader subgraph representing the diffuse value of this light.
		 */
		public function computeDiffuseInWorldSpace(lightId : uint) : SFloat
		{
			throw new Error('Must be overriden');
		}

		/**
		 * Creates the shader subgraph to compute the specular value of a given light.
		 * Computation will be done in tangent space.
		 *
		 * @param lightId
		 * @return
		 */
		public function computeSpecularInTangentSpace(lightId : uint) : SFloat
		{
			throw new Error('Must be overriden');
		}

		/**
		 * Creates the shader subgraph to compute the specular value of a given light.
		 * Computation will be done in local space.
		 *
		 * @param lightId
		 * @return
		 */
		public function computeSpecularInLocalSpace(lightId : uint) : SFloat
		{
			throw new Error('Must be overriden');
		}

		/**
		 * Creates the shader subgraph to compute the specular value of a given light.
		 * Computation will be done in world space.
		 *
		 * @param lightId
		 * @return
		 */
		public function computeSpecularInWorldSpace(lightId : uint) : SFloat
		{
			throw new Error('Must be overriden');
		}

		/**
		 * Compute final diffuse value from light direction and normal.
		 * Both requested vector can be in any space (tangent, local, light, view or whatever) but must be in the same space.
		 * Also they must be recheable in the fragment shader (they must be constant, or already interpolated)
		 *
		 * @param lightId
		 * @param fsLightDirection
		 * @param fsNormal
		 * @return
		 */
		protected function diffuseFromVectors(lightId			: uint,
											  fsLightDirection	: SFloat,
											  fsNormal			: SFloat) : SFloat
		{
			var fsLambertProduct	: SFloat = saturate(negate(dotProduct3(fsLightDirection, fsNormal)));
			var cDiffuse			: SFloat = getLightParameter(lightId, 'diffuse', 1);

			if (meshBindings.propertyExists(PhongProperties.DIFFUSE_MULTIPLIER))
				cDiffuse.scaleBy(meshBindings.getParameter(PhongProperties.DIFFUSE_MULTIPLIER, 1));

			return multiply(cDiffuse, fsLambertProduct);
		}

		/**
		 * Compute final specular value from light direction, normal, and camera direction.
		 * All three requested vector can be in any space (tangent, local, light, view or whatever) but must all be in the same sapce.
		 * Also they must be recheable in the fragment shader (they must be constant, or already interpolated)
		 *
		 * @param lightId
		 * @param fsLightDirection
		 * @param fsNormal
		 * @param fsCameraDirection
		 * @return
		 */
		protected function specularFromVectors(lightId				: uint,
											   fsLightDirection		: SFloat,
											   fsNormal				: SFloat,
											   fsCameraDirection	: SFloat) : SFloat
		{
			var fsLightReflectedDirection	: SFloat = reflect(fsLightDirection, fsNormal);
			var fsLambertProduct			: SFloat = saturate(negate(dotProduct3(fsLightReflectedDirection, fsCameraDirection)));

			var cLightSpecular	: SFloat 	= getLightParameter(lightId, 'specular', 1);
			var cLightShininess	: SFloat 	= getLightParameter(lightId, 'shininess', 1);

			if (meshBindings.propertyExists(PhongProperties.SPECULAR))
			{
				var specular	: SFloat	= meshBindings.getParameter(PhongProperties.SPECULAR, 3);

				cLightSpecular = multiply(cLightSpecular, specular);
			}

			if (meshBindings.propertyExists(PhongProperties.SPECULAR_MAP))
			{
				var fsSpecularSample 	: SFloat	= sampleTexture(
					meshBindings.getTextureParameter(
						PhongProperties.SPECULAR_MAP,
						1,
						0,
						1,
						0,
						meshBindings.getProperty(PhongProperties.SPECULAR_MAP_FORMAT, SamplerFormat.RGBA)
					),
					fsUV
				);

				cLightSpecular.scaleBy(fsSpecularSample.x);
			}

			if (meshBindings.propertyExists(PhongProperties.SHININESS))
				cLightShininess.scaleBy(meshBindings.getParameter(PhongProperties.SHININESS, 1));

			return multiply(cLightSpecular, power(fsLambertProduct, cLightShininess));
		}
	}
}

package aerys.minko.render.shader.part.phong.contribution
{
	import aerys.minko.render.material.basic.BasicProperties;
	import aerys.minko.render.material.phong.PhongProperties;
	import aerys.minko.render.shader.SFloat;
	import aerys.minko.render.shader.Shader;
	import aerys.minko.render.shader.part.phong.LightAwareShaderPart;
	import aerys.minko.type.enum.SamplerFiltering;
	import aerys.minko.type.enum.SamplerFormat;

	/**
	 * Methods in this class allow to compute light contributions.
	 * It is extended by InfiniteShaderPart for directional lights, and LocalizedShaderPart for point and spot lights.
	 *
	 * All methods are available in tangent, local and world space.
	 *
	 * - Computing in tangent space is computationally more expensive, both in CPU and GPU, because we need to feed
	 * more data to the shaders than the other methods but it allows to do both bump and parallax mapping.
	 *
	 * - Doing it in local space is computationally more expensive on the CPU, because the light
	 * position will need to be converted in the CPU for each mesh and at each frame will produce the lighter shaders.
	 *
	 * Doing as much work on the CPU as possible, and have as short a possible shader would be the way to go
	 * if adobe's compiler was better than it is. Therefor, minko defaults to computing everything in world space
	 * which is faster in practical tests.
	 *
	 * The cost of each light will be in O(number of drawcalls) on the CPU.
	 * However, this is the way that will lead to the smaller shaders, and is therefor recomended when used
	 * in small scenes with many lights.
	 *
	 * - Computing lights in world space is cheaper on the CPU, because no extra computation is
	 * needed for each mesh, but will cost one extra matrix multiplication on the GPU (we need the world position
	 * or every fragment).
	 *
	 * @author Romain Gilliotte
	 *
	 */
	public class AbstractContributionShaderPart extends LightAwareShaderPart
	{

		public function AbstractContributionShaderPart(main : Shader)
		{
			super(main);
		}

		/**
		 * Creates the shader subgraph to compute the diffuse value of a given light.
		 * Computations will be done in tangent space.
		 *
		 * @param lightId The id of the localized light (PointLight or SpotLight)
		 * @return Shader subgraph representing the diffuse value of this light.
		 */
		public function computeDiffuseInTangentSpace(lightId : uint) : SFloat
		{
			throw new Error('Must be overriden');
		}

		/**
		 * Creates the shader subgraph to compute the diffuse value of a given light.
		 * Computation will be done in local space.
		 *
		 * @param lightId The id of the localized light (PointLight or SpotLight)
		 * @return Shader subgraph representing the diffuse value of this light.
		 */
		public function computeDiffuseInLocalSpace(lightId : uint) : SFloat
		{
			throw new Error('Must be overriden');
		}

		/**
		 * Creates the shader subgraph to compute the diffuse value of a given light.
		 * Computation will be done in world space.
		 *
		 * @param lightId The id of the light
		 * @return Shader subgraph representing the diffuse value of this light.
		 */
		public function computeDiffuseInWorldSpace(lightId : uint) : SFloat
		{
			throw new Error('Must be overriden');
		}

		/**
		 * Creates the shader subgraph to compute the specular value of a given light.
		 * Computation will be done in tangent space.
		 *
		 * @param lightId
		 * @return
		 */
		public function computeSpecularInTangentSpace(lightId : uint) : SFloat
		{
			throw new Error('Must be overriden');
		}

		/**
		 * Creates the shader subgraph to compute the specular value of a given light.
		 * Computation will be done in local space.
		 *
		 * @param lightId
		 * @return
		 */
		public function computeSpecularInLocalSpace(lightId : uint) : SFloat
		{
			throw new Error('Must be overriden');
		}

		/**
		 * Creates the shader subgraph to compute the specular value of a given light.
		 * Computation will be done in world space.
		 *
		 * @param lightId
		 * @return
		 */
		public function computeSpecularInWorldSpace(lightId : uint) : SFloat
		{
			throw new Error('Must be overriden');
		}

		/**
		 * Compute final diffuse value from light direction and normal.
		 * Both requested vector can be in any space (tangent, local, light, view or whatever) but must be in the same space.
		 * Also they must be recheable in the fragment shader (they must be constant, or already interpolated)
		 *
		 * @param lightId
		 * @param fsLightDirection
		 * @param fsNormal
		 * @return
		 */
		protected function diffuseFromVectors(lightId			: uint,
											  fsLightDirection	: SFloat,
											  fsNormal			: SFloat) : SFloat
		{
			var fsLambertProduct	: SFloat = saturate(negate(dotProduct3(fsLightDirection, fsNormal)));
			var cDiffuse			: SFloat = getLightParameter(lightId, 'diffuse', 1);

			if (meshBindings.propertyExists(PhongProperties.DIFFUSE_MULTIPLIER))
				cDiffuse.scaleBy(meshBindings.getParameter(PhongProperties.DIFFUSE_MULTIPLIER, 1));

			return multiply(cDiffuse, fsLambertProduct);
		}

		/**
		 * Compute final specular value from light direction, normal, and camera direction.
		 * All three requested vector can be in any space (tangent, local, light, view or whatever) but must all be in the same sapce.
		 * Also they must be recheable in the fragment shader (they must be constant, or already interpolated)
		 *
		 * @param lightId
		 * @param fsLightDirection
		 * @param fsNormal
		 * @param fsCameraDirection
		 * @return
		 */
		protected function specularFromVectors(lightId				: uint,
											   fsLightDirection		: SFloat,
											   fsNormal				: SFloat,
											   fsCameraDirection	: SFloat) : SFloat
		{
			var fsLightReflectedDirection	: SFloat = reflect(fsLightDirection, fsNormal);
			var fsLambertProduct			: SFloat = saturate(negate(dotProduct3(fsLightReflectedDirection, fsCameraDirection)));

			var cLightSpecular	: SFloat 	= getLightParameter(lightId, 'specular', 1);
			var cLightShininess	: SFloat 	= getLightParameter(lightId, 'shininess', 1);

			if (meshBindings.propertyExists(PhongProperties.SPECULAR))
			{
				var specular	: SFloat	= meshBindings.getParameter(PhongProperties.SPECULAR, 4);

				cLightSpecular = multiply(cLightSpecular, specular.xyz);
			}

			if (meshBindings.propertyExists(PhongProperties.SPECULAR_MAP))
			{
				var fsSpecularSample 	: SFloat	= sampleTexture(
					meshBindings.getTextureParameter(
						PhongProperties.SPECULAR_MAP,
						1,
						0,
						1,
						0,
						meshBindings.getProperty(PhongProperties.SPECULAR_MAP_FORMAT, SamplerFormat.RGBA)
					),
					fsUV
				);

				cLightSpecular.scaleBy(fsSpecularSample.x);
			}

			if (meshBindings.propertyExists(PhongProperties.SHININESS))
				cLightShininess.scaleBy(meshBindings.getParameter(PhongProperties.SHININESS, 1));

			return multiply(cLightSpecular, power(fsLambertProduct, cLightShininess));
		}
	}
}

Fix specular color value

Fix specular color 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.model.PortalModel;
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.Fault;
import mx.rpc.Responder;
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 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;

            getServiceInfo();
        }

        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;

            getServiceInfo();
        }
    }

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

    private function getServiceInfo():void
    {
        var serviceInfoRequest:JSONTask = new JSONTask();
        serviceInfoRequest.url = extractServiceInfoURL(serviceDirectoryBuildRequest.url);
        const param:URLVariables = new URLVariables();
        param.f = 'json';
        serviceInfoRequest.execute(param, new Responder(serviceInfoRequest_resultHandler, serviceInfoRequest_faultHandler));

        function serviceInfoRequest_resultHandler(serverInfo:Object):void
        {
            owningSystemURL = serverInfo.owningSystemUrl;
            if (PortalModel.getInstance().hasSameOrigin(owningSystemURL))
            {
                if (PortalModel.getInstance().portal.signedIn)
                {
                    IdentityManager.instance.getCredential(
                        serviceDirectoryBuildRequest.url, false,
                        new Responder(getCredential_successHandler, getCredential_faultHandler));

                    function getCredential_successHandler(credential:Credential):void
                    {
                        checkIfServiceIsSecure(serverInfo);
                    }

                    function getCredential_faultHandler(fault:Fault):void
                    {
                        checkIfServiceIsSecure(serverInfo);
                    }
                }
                else
                {
                    var credential:Credential = IdentityManager.instance.findCredential(serviceDirectoryBuildRequest.url);
                    if (credential)
                    {
                        credential.destroy();
                    }
                    checkIfServiceIsSecure(serverInfo);
                }
            }
            else
            {
                checkIfServiceIsSecure(serverInfo);
            }
        }

        function serviceInfoRequest_faultHandler(fault:Fault):void
        {
            checkIfServiceIsSecure(null);
        }
    }

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

        if (serverInfo)
        {
            isServiceSecured = (serverInfo.currentVersion >= 10.01
                && serverInfo.authInfo
                && serverInfo.authInfo.isTokenBasedSecurity);

            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();
        }
        else
        {
            //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);
    }
}
}

////////////////////////////////////////////////////////////////////////////////
// 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.model.PortalModel;
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.Fault;
import mx.rpc.Responder;
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 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;

            getServiceInfo();
        }

        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;

            getServiceInfo();
        }
    }

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

    private function getServiceInfo():void
    {
        var serviceInfoRequest:JSONTask = new JSONTask();
        serviceInfoRequest.url = extractServiceInfoURL(serviceDirectoryBuildRequest.url);
        const param:URLVariables = new URLVariables();
        param.f = 'json';
        serviceInfoRequest.execute(param, new Responder(serviceInfoRequest_resultHandler, serviceInfoRequest_faultHandler));

        function serviceInfoRequest_resultHandler(serverInfo:Object):void
        {
            owningSystemURL = serverInfo.owningSystemUrl;
            if (PortalModel.getInstance().hasSameOrigin(owningSystemURL))
            {
                if (PortalModel.getInstance().portal.signedIn)
                {
                    IdentityManager.instance.getCredential(
                        serviceDirectoryBuildRequest.url, false,
                        new Responder(getCredential_successHandler, getCredential_faultHandler));

                    function getCredential_successHandler(credential:Credential):void
                    {
                        checkIfServiceIsSecure(serverInfo);
                    }

                    function getCredential_faultHandler(fault:Fault):void
                    {
                        checkIfServiceIsSecure(serverInfo);
                    }
                }
                else
                {
                    var credential:Credential = IdentityManager.instance.findCredential(serviceDirectoryBuildRequest.url);
                    if (credential)
                    {
                        credential.destroy();
                    }
                    checkIfServiceIsSecure(serverInfo);
                }
            }
            else
            {
                checkIfServiceIsSecure(serverInfo);
            }
        }

        function serviceInfoRequest_faultHandler(fault:Fault):void
        {
            checkIfServiceIsSecure(null);
        }
    }

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

        if (serverInfo)
        {
            isServiceSecured = (serverInfo.currentVersion >= 10.01
                && serverInfo.authInfo
                && serverInfo.authInfo.isTokenBasedSecurity
                && (serverInfo.authInfo.tokenServicesUrl || serverInfo.authInfo.tokenServiceUrl));

            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();
                const tokenServiceURL:String = serverInfo.authInfo.tokenServicesUrl || serverInfo.authInfo.tokenServiceUrl;
                tokenServiceCrossDomainRequest.url = extractCrossDomainPolicyFileURL(tokenServiceURL);
                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();
        }
        else
        {
            //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);
    }
}
}

Use either tokenServicesUrl or tokenServiceUrl when building the token service cross-domain policy file URL.


package
{
    import flash.display.Sprite;
    import flash.text.TextField;
    import flash.net.Socket;
    import flash.events.Event;
    import flash.events.IOErrorEvent;
    import flash.events.ProgressEvent;
    import flash.events.SecurityErrorEvent;
    import flash.utils.ByteArray;
    import flash.utils.setTimeout;

    [SWF(width="640", height="480")]
    public class swfcat extends Sprite
    {
        /* David's bridge (nickname eRYaZuvY02FpExln) that also serves a
           crossdomain policy. */
        private const DEFAULT_TOR_ADDR:Object = {
            // host: "173.255.221.44", 3VXRyxz67OeRoqHn
            host: "69.164.193.231",
            port: 9001
        };

        // Milliseconds.
        private const FACILITATOR_POLL_INTERVAL:int = 10000;

        // Socket to facilitator.
        private var s_f:Socket;

        private var output_text:TextField;

        private var fac_addr:Object;

        public function puts(s:String):void
        {
            output_text.appendText(s + "\n");
            output_text.scrollV = output_text.maxScrollV;
        }

        public function swfcat()
        {
            output_text = new TextField();
            output_text.width = 640;
            output_text.height = 480;
            output_text.background = true;
            output_text.backgroundColor = 0x001f0f;
            output_text.textColor = 0x44CC44;
            addChild(output_text);

            puts("Starting.");
            // Wait until the query string parameters are loaded.
            this.loaderInfo.addEventListener(Event.COMPLETE, loaderinfo_complete);
        }

        private function loaderinfo_complete(e:Event):void
        {
            var fac_spec:String;

            puts("Parameters loaded.");
            fac_spec = this.loaderInfo.parameters["facilitator"];
            if (!fac_spec) {
                puts("Error: no \"facilitator\" specification provided.");
                return;
            }
            puts("Facilitator spec: \"" + fac_spec + "\"");
            fac_addr = parse_addr_spec(fac_spec);
            if (!fac_addr) {
                puts("Error: Facilitator spec must be in the form \"host:port\".");
                return;
            }

            main();
        }

        /* The main logic begins here, after start-up issues are taken care of. */
        private function main():void
        {
            s_f = new Socket();

            s_f.addEventListener(Event.CONNECT, fac_connected);
            s_f.addEventListener(Event.CLOSE, function (e:Event):void {
                puts("Facilitator: closed connection.");
                setTimeout(main, FACILITATOR_POLL_INTERVAL);
            });
            s_f.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
                puts("Facilitator: I/O error: " + e.text + ".");
            });
            s_f.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
                puts("Facilitator: security error: " + e.text + ".");
            });

            puts("Facilitator: connecting to " + fac_addr.host + ":" + fac_addr.port + ".");
            s_f.connect(fac_addr.host, fac_addr.port);
        }

        private function fac_connected(e:Event):void
        {
            puts("Facilitator: connected.");

            s_f.addEventListener(ProgressEvent.SOCKET_DATA, fac_data);

            s_f.writeUTFBytes("GET / HTTP/1.0\r\n\r\n");
        }

        private function fac_data(e:ProgressEvent):void
        {
            var client_spec:String;
            var client_addr:Object;
            var proxy_pair:Object;

            client_spec = s_f.readMultiByte(e.bytesLoaded, "utf-8");
            puts("Facilitator: got \"" + client_spec + "\".");

            client_addr = parse_addr_spec(client_spec);
            if (!client_addr) {
                puts("Error: Client spec must be in the form \"host:port\".");
                return;
            }
            if (client_addr.host == "0.0.0.0" && client_addr.port == 0) {
                puts("Error: Facilitator has no clients.");
                return;
            }

            proxy_pair = new ProxyPair(this, client_addr, DEFAULT_TOR_ADDR);
            proxy_pair.connect();
        }

        /* Parse an address in the form "host:port". Returns an Object with
           keys "host" (String) and "port" (int). Returns null on error. */
        private static function parse_addr_spec(spec:String):Object
        {
            var parts:Array;
            var addr:Object;

            parts = spec.split(":", 2);
            if (parts.length != 2 || !parseInt(parts[1]))
                return null;
            addr = {}
            addr.host = parts[0];
            addr.port = parseInt(parts[1]);

            return addr;
        }
    }
}

import flash.display.Sprite;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.ProgressEvent;
import flash.events.SecurityErrorEvent;
import flash.net.Socket;
import flash.utils.ByteArray;

/* An instance of a client-relay connection. */
class ProxyPair
{
    // Address ({host, port}) of client.
    private var addr_c:Object;
    // Address ({host, port}) of relay.
    private var addr_r:Object;

    // Socket to client.
    private var s_c:Socket;
    // Socket to relay.
    private var s_r:Socket;

    // Parent swfcat, for UI updates.
    private var ui:swfcat;

    private function log(msg:String):void
    {
        ui.puts(id() + ": " + msg)
    }

    // String describing this pair for output.
    private function id():String
    {
        return "<" + this.addr_c.host + ":" + this.addr_c.port +
            "," + this.addr_r.host + ":" + this.addr_r.port + ">";
    }

    public function ProxyPair(ui:swfcat, addr_c:Object, addr_r:Object)
    {
        this.ui = ui;
        this.addr_c = addr_c;
        this.addr_r = addr_r;
    }

    public function connect():void
    {
        s_r = new Socket();

        s_r.addEventListener(Event.CONNECT, tor_connected);
        s_r.addEventListener(Event.CLOSE, function (e:Event):void {
            log("Tor: closed.");
            if (s_c.connected)
                s_c.close();
        });
        s_r.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
            log("Tor: I/O error: " + e.text + ".");
            if (s_c.connected)
                s_c.close();
        });
        s_r.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
            log("Tor: security error: " + e.text + ".");
            if (s_c.connected)
                s_c.close();
        });

        log("Tor: connecting to " + addr_r.host + ":" + addr_r.port + ".");
        s_r.connect(addr_r.host, addr_r.port);
    }

    private function tor_connected(e:Event):void
    {
        log("Tor: connected.");

        s_c = new Socket();

        s_c.addEventListener(Event.CONNECT, client_connected);
        s_c.addEventListener(Event.CLOSE, function (e:Event):void {
            log("Client: closed.");
            if (s_r.connected)
                s_r.close();
        });
        s_c.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
            log("Client: I/O error: " + e.text + ".");
            if (s_r.connected)
                s_r.close();
        });
        s_c.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
            log("Client: security error: " + e.text + ".");
            if (s_r.connected)
                s_r.close();
        });

        log("Client: connecting to " + addr_c.host + ":" + addr_c.port + ".");
        s_c.connect(addr_c.host, addr_c.port);
    }

    private function client_connected(e:Event):void
    {
        log("Client: connected.");

        s_r.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
            var bytes:ByteArray = new ByteArray();
            s_r.readBytes(bytes, 0, e.bytesLoaded);
            log("Tor: read " + bytes.length + ".");
            s_c.writeBytes(bytes);
        });
        s_c.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
            var bytes:ByteArray = new ByteArray();
            s_c.readBytes(bytes, 0, e.bytesLoaded);
            log("Client: read " + bytes.length + ".");
            s_r.writeBytes(bytes);
        });
    }
}

package
{
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.text.TextField;
    import flash.net.Socket;
    import flash.events.Event;
    import flash.events.IOErrorEvent;
    import flash.events.ProgressEvent;
    import flash.events.SecurityErrorEvent;
    import flash.utils.ByteArray;
    import flash.utils.setTimeout;

    public class swfcat extends Sprite
    {
        /* David's bridge (nickname eRYaZuvY02FpExln) that also serves a
           crossdomain policy. */
        private const DEFAULT_TOR_ADDR:Object = {
            // host: "173.255.221.44", 3VXRyxz67OeRoqHn
            host: "69.164.193.231",
            port: 9001
        };

        // Milliseconds.
        private const FACILITATOR_POLL_INTERVAL:int = 10000;

        // Socket to facilitator.
        private var s_f:Socket;

        private var output_text:TextField;

        private var fac_addr:Object;

        public function puts(s:String):void
        {
            output_text.appendText(s + "\n");
            output_text.scrollV = output_text.maxScrollV;
        }

        public function swfcat()
        {
            // Absolute positioning.
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.align = StageAlign.TOP_LEFT;

            output_text = new TextField();
            output_text.width = stage.stageWidth;
            output_text.height = stage.stageHeight;
            output_text.background = true;
            output_text.backgroundColor = 0x001f0f;
            output_text.textColor = 0x44CC44;
            addChild(output_text);

            puts("Starting.");
            // Wait until the query string parameters are loaded.
            this.loaderInfo.addEventListener(Event.COMPLETE, loaderinfo_complete);
        }

        private function loaderinfo_complete(e:Event):void
        {
            var fac_spec:String;

            puts("Parameters loaded.");
            fac_spec = this.loaderInfo.parameters["facilitator"];
            if (!fac_spec) {
                puts("Error: no \"facilitator\" specification provided.");
                return;
            }
            puts("Facilitator spec: \"" + fac_spec + "\"");
            fac_addr = parse_addr_spec(fac_spec);
            if (!fac_addr) {
                puts("Error: Facilitator spec must be in the form \"host:port\".");
                return;
            }

            main();
        }

        /* The main logic begins here, after start-up issues are taken care of. */
        private function main():void
        {
            s_f = new Socket();

            s_f.addEventListener(Event.CONNECT, fac_connected);
            s_f.addEventListener(Event.CLOSE, function (e:Event):void {
                puts("Facilitator: closed connection.");
                setTimeout(main, FACILITATOR_POLL_INTERVAL);
            });
            s_f.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
                puts("Facilitator: I/O error: " + e.text + ".");
            });
            s_f.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
                puts("Facilitator: security error: " + e.text + ".");
            });

            puts("Facilitator: connecting to " + fac_addr.host + ":" + fac_addr.port + ".");
            s_f.connect(fac_addr.host, fac_addr.port);
        }

        private function fac_connected(e:Event):void
        {
            puts("Facilitator: connected.");

            s_f.addEventListener(ProgressEvent.SOCKET_DATA, fac_data);

            s_f.writeUTFBytes("GET / HTTP/1.0\r\n\r\n");
        }

        private function fac_data(e:ProgressEvent):void
        {
            var client_spec:String;
            var client_addr:Object;
            var proxy_pair:Object;

            client_spec = s_f.readMultiByte(e.bytesLoaded, "utf-8");
            puts("Facilitator: got \"" + client_spec + "\".");

            client_addr = parse_addr_spec(client_spec);
            if (!client_addr) {
                puts("Error: Client spec must be in the form \"host:port\".");
                return;
            }
            if (client_addr.host == "0.0.0.0" && client_addr.port == 0) {
                puts("Error: Facilitator has no clients.");
                return;
            }

            proxy_pair = new ProxyPair(this, client_addr, DEFAULT_TOR_ADDR);
            proxy_pair.connect();
        }

        /* Parse an address in the form "host:port". Returns an Object with
           keys "host" (String) and "port" (int). Returns null on error. */
        private static function parse_addr_spec(spec:String):Object
        {
            var parts:Array;
            var addr:Object;

            parts = spec.split(":", 2);
            if (parts.length != 2 || !parseInt(parts[1]))
                return null;
            addr = {}
            addr.host = parts[0];
            addr.port = parseInt(parts[1]);

            return addr;
        }
    }
}

import flash.display.Sprite;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.ProgressEvent;
import flash.events.SecurityErrorEvent;
import flash.net.Socket;
import flash.utils.ByteArray;

/* An instance of a client-relay connection. */
class ProxyPair
{
    // Address ({host, port}) of client.
    private var addr_c:Object;
    // Address ({host, port}) of relay.
    private var addr_r:Object;

    // Socket to client.
    private var s_c:Socket;
    // Socket to relay.
    private var s_r:Socket;

    // Parent swfcat, for UI updates.
    private var ui:swfcat;

    private function log(msg:String):void
    {
        ui.puts(id() + ": " + msg)
    }

    // String describing this pair for output.
    private function id():String
    {
        return "<" + this.addr_c.host + ":" + this.addr_c.port +
            "," + this.addr_r.host + ":" + this.addr_r.port + ">";
    }

    public function ProxyPair(ui:swfcat, addr_c:Object, addr_r:Object)
    {
        this.ui = ui;
        this.addr_c = addr_c;
        this.addr_r = addr_r;
    }

    public function connect():void
    {
        s_r = new Socket();

        s_r.addEventListener(Event.CONNECT, tor_connected);
        s_r.addEventListener(Event.CLOSE, function (e:Event):void {
            log("Tor: closed.");
            if (s_c.connected)
                s_c.close();
        });
        s_r.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
            log("Tor: I/O error: " + e.text + ".");
            if (s_c.connected)
                s_c.close();
        });
        s_r.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
            log("Tor: security error: " + e.text + ".");
            if (s_c.connected)
                s_c.close();
        });

        log("Tor: connecting to " + addr_r.host + ":" + addr_r.port + ".");
        s_r.connect(addr_r.host, addr_r.port);
    }

    private function tor_connected(e:Event):void
    {
        log("Tor: connected.");

        s_c = new Socket();

        s_c.addEventListener(Event.CONNECT, client_connected);
        s_c.addEventListener(Event.CLOSE, function (e:Event):void {
            log("Client: closed.");
            if (s_r.connected)
                s_r.close();
        });
        s_c.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
            log("Client: I/O error: " + e.text + ".");
            if (s_r.connected)
                s_r.close();
        });
        s_c.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
            log("Client: security error: " + e.text + ".");
            if (s_r.connected)
                s_r.close();
        });

        log("Client: connecting to " + addr_c.host + ":" + addr_c.port + ".");
        s_c.connect(addr_c.host, addr_c.port);
    }

    private function client_connected(e:Event):void
    {
        log("Client: connected.");

        s_r.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
            var bytes:ByteArray = new ByteArray();
            s_r.readBytes(bytes, 0, e.bytesLoaded);
            log("Tor: read " + bytes.length + ".");
            s_c.writeBytes(bytes);
        });
        s_c.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
            var bytes:ByteArray = new ByteArray();
            s_c.readBytes(bytes, 0, e.bytesLoaded);
            log("Client: read " + bytes.length + ".");
            s_r.writeBytes(bytes);
        });
    }
}

Remove hardcoded width and height from SWF source.

Let it be completely controlled by containing HTML.


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

import com.esri.builder.model.CustomWidgetType;
import com.esri.builder.model.Model;
import com.esri.builder.model.WidgetType;
import com.esri.builder.model.WidgetTypeRegistryModel;
import com.esri.builder.supportClasses.LogUtil;
import com.esri.builder.views.BuilderAlert;

import flash.events.EventDispatcher;
import flash.filesystem.File;
import flash.filesystem.FileMode;
import flash.filesystem.FileStream;
import flash.system.ApplicationDomain;
import flash.utils.ByteArray;

import modules.IBuilderModule;
import modules.supportClasses.CustomXMLModule;

import mx.core.FlexGlobals;
import mx.events.ModuleEvent;
import mx.logging.ILogger;
import mx.logging.Log;
import mx.modules.IModuleInfo;
import mx.modules.ModuleManager;
import mx.resources.ResourceManager;

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

    private var widgetTypeArr:Array = [];
    private var pendingModuleInfos:Array = [];
    private var modulesToLoad:uint;

    public function loadWidgetTypes():void
    {
        if (Log.isInfo())
        {
            LOG.info('Loading modules...');
        }

        var moduleFiles:Array = getModuleFiles(WellKnownDirectories.getInstance().bundledModules)
            .concat(getModuleFiles(WellKnownDirectories.getInstance().customModules));

        //TODO: remove XML files from found SWF files
        modulesToLoad = moduleFiles.length;

        // Load the found modules.
        for each (var moduleFile:File in moduleFiles)
        {
            if (Log.isDebug())
            {
                LOG.debug('loading module {0}', moduleFile.url);
            }

            //we assume XML module files are custom
            if (moduleFile.extension == "xml")
            {
                loadCustomWidgetTypeConfig(moduleFile);
            }
            else if (moduleFile.extension == "swf")
            {
                loadModule(moduleFile);
            }
        }

        checkIfNoMoreModuleInfosLeft();
    }

    private function loadModule(moduleFile:File):void
    {
        var fileBytes:ByteArray = new ByteArray();
        var fileStream:FileStream = new FileStream();
        fileStream.open(moduleFile, FileMode.READ);
        fileStream.readBytes(fileBytes);
        fileStream.close();

        const moduleInfo:IModuleInfo = ModuleManager.getModule(moduleFile.url);
        pendingModuleInfos.push(moduleInfo);
        moduleInfo.addEventListener(ModuleEvent.READY, moduleInfo_readyHandler);
        moduleInfo.addEventListener(ModuleEvent.ERROR, moduleInfo_errorHandler);
        moduleInfo.load(ApplicationDomain.currentDomain, null, fileBytes, FlexGlobals.topLevelApplication.moduleFactory);
    }

    public function loadCustomWidgetTypeConfig(configFile:File):void
    {
        var customWidgetType:CustomWidgetType = createCustomWidgetTypeFromConfig(configFile);
        if (customWidgetType)
        {
            WidgetTypeRegistryModel.getInstance().widgetTypeRegistry.addWidgetType(customWidgetType);
        }

        markModuleAsLoaded();
    }

    private function createCustomWidgetTypeFromConfig(configFile:File):CustomWidgetType
    {
        var fileStream:FileStream = new FileStream();
        var customWidgetType:CustomWidgetType;
        try
        {
            fileStream.open(configFile, FileMode.READ);
            const configXML:XML = XML(fileStream.readUTFBytes(fileStream.bytesAvailable));
            customWidgetType = parseCustomWidgetType(configXML);
        }
        catch (e:Error)
        {
            if (Log.isWarn())
            {
                LOG.warn('Error creating custom module {0}', configFile.nativePath);
            }
        }
        finally
        {
            fileStream.close();
        }

        return customWidgetType;
    }

    private function parseCustomWidgetType(configXML:XML):CustomWidgetType
    {
        var customModule:CustomXMLModule = new CustomXMLModule();
        customModule.widgetName = configXML.name;
        customModule.isOpenByDefault = (configXML.openbydefault == 'true');

        var widgetLabel:String = configXML.label[0];
        var widgetDescription:String = configXML.description[0];
        var widgetHelpURL:String = configXML.helpurl[0];
        var widgetConfiguration:XML = configXML.configuration[0] ? configXML.configuration[0] : <configuration/>;
        var widgetVersion:String = configXML.widgetversion[0];

        customModule.widgetIconLocation = createWidgetIconPath(configXML.icon[0], customModule.widgetName);
        customModule.widgetLabel = widgetLabel ? widgetLabel : customModule.widgetName;
        customModule.widgetDescription = widgetDescription ? widgetDescription : "";
        customModule.widgetHelpURL = widgetHelpURL ? widgetHelpURL : "";

        return new CustomWidgetType(customModule, widgetVersion, widgetConfiguration);
    }

    private function createWidgetIconPath(iconPath:String, widgetName:String):String
    {
        var widgetIconPath:String;

        if (iconPath)
        {
            widgetIconPath = "widgets/" + widgetName + "/" + iconPath;
        }
        else
        {
            widgetIconPath = "assets/images/i_widget.png";
        }

        return widgetIconPath;
    }

    /* Finds module files at the parent-directory level */
    private function getModuleFiles(directory:File):Array
    {
        var moduleFiles:Array = [];

        if (directory.isDirectory)
        {
            const files:Array = directory.getDirectoryListing();

            for each (var file:File in files)
            {
                if (isModuleFile(file))
                {
                    moduleFiles.push(file);
                }
            }
        }

        return moduleFiles;
    }

    private function isModuleFile(file:File):Boolean
    {
        const moduleFileName:RegExp = /^.*Module\.(swf|xml)$/;
        return !file.isDirectory && (moduleFileName.test(file.name));
    }

    private function moduleInfo_readyHandler(event:ModuleEvent):void
    {
        var moduleInfo:IModuleInfo = event.currentTarget as IModuleInfo;
        moduleInfo.removeEventListener(ModuleEvent.READY, moduleInfo_readyHandler);
        moduleInfo.removeEventListener(ModuleEvent.ERROR, moduleInfo_errorHandler);

        const builderModule:IBuilderModule = event.module.factory.create() as IBuilderModule;
        if (builderModule)
        {
            var customModulesDirectoryURL:String = WellKnownDirectories.getInstance().customModules.url;
            var isCustomModule:Boolean = (moduleInfo.url.indexOf(customModulesDirectoryURL) > -1);
            const widgetType:WidgetType = isCustomModule ?
                new CustomWidgetType(builderModule) :
                new WidgetType(builderModule);
            widgetTypeArr.push(widgetType);

            if (Log.isDebug())
            {
                LOG.debug('Module {0} is resolved', widgetType.name);
            }

        }

        markModuleAsLoaded();
        removeModuleInfo(moduleInfo);
        checkIfNoMoreModuleInfosLeft();
    }

    private function removeModuleInfo(moduleInfo:IModuleInfo):void
    {
        const index:int = pendingModuleInfos.indexOf(moduleInfo);
        if (index > -1)
        {
            pendingModuleInfos.splice(index, 1);
        }
    }

    private function checkIfNoMoreModuleInfosLeft():void
    {
        if (modulesToLoad === 0)
        {
            sortAndAssignWidgetTypes();
        }
    }

    private function markModuleAsLoaded():void
    {
        modulesToLoad--;
    }

    private function sortAndAssignWidgetTypes():void
    {
        if (Log.isInfo())
        {
            LOG.info('All modules resolved');
        }

        widgetTypeArr.sort(compareWidgetTypes);

        var widgetTypes:Array = widgetTypeArr.filter(widgetTypeFilter);
        for each (var widgetType:WidgetType in widgetTypes)
        {
            WidgetTypeRegistryModel.getInstance().widgetTypeRegistry.addWidgetType(widgetType);
        }

        var layoutWidgetTypes:Array = widgetTypeArr.filter(layoutWidgetTypeFilter);
        for each (var layoutWidgetType:WidgetType in layoutWidgetTypes)
        {
            WidgetTypeRegistryModel.getInstance().layoutWidgetTypeRegistry.addWidgetType(layoutWidgetType);
        }

        function widgetTypeFilter(item:WidgetType, index:int, source:Array):Boolean
        {
            return item.isManaged;
        }
        function layoutWidgetTypeFilter(item:WidgetType, index:int, source:Array):Boolean
        {
            return !item.isManaged;
        }

        dispatchEvent(new WidgetTypeLoaderEvent(WidgetTypeLoaderEvent.LOAD_TYPES_COMPLETE));
    }

    private function compareWidgetTypes(lhs:WidgetType, rhs:WidgetType):int
    {
        const lhsLabel:String = lhs.label.toLowerCase();
        const rhsLabel:String = rhs.label.toLowerCase();
        if (lhsLabel < rhsLabel)
        {
            return -1;
        }
        if (lhsLabel > rhsLabel)
        {
            return 1;
        }
        return 0;
    }

    private function moduleInfo_errorHandler(event:ModuleEvent):void
    {
        if (Log.isWarn())
        {
            LOG.warn('Module error: {0}', event.errorText);
        }

        var moduleInfo:IModuleInfo = event.currentTarget as IModuleInfo;
        moduleInfo.removeEventListener(ModuleEvent.ERROR, moduleInfo_errorHandler);
        moduleInfo.removeEventListener(ModuleEvent.READY, moduleInfo_readyHandler);

        Model.instance.status = event.errorText;
        BuilderAlert.show(event.errorText,
                          ResourceManager.getInstance().getString('BuilderStrings', 'error'));
        markModuleAsLoaded();
        removeModuleInfo(moduleInfo);
        checkIfNoMoreModuleInfosLeft();
    }
}
}

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

import com.esri.builder.model.CustomWidgetType;
import com.esri.builder.model.Model;
import com.esri.builder.model.WidgetType;
import com.esri.builder.model.WidgetTypeRegistryModel;
import com.esri.builder.supportClasses.LogUtil;
import com.esri.builder.views.BuilderAlert;

import flash.events.EventDispatcher;
import flash.filesystem.File;
import flash.filesystem.FileMode;
import flash.filesystem.FileStream;
import flash.system.ApplicationDomain;
import flash.utils.ByteArray;

import modules.IBuilderModule;
import modules.supportClasses.CustomXMLModule;

import mx.core.FlexGlobals;
import mx.events.ModuleEvent;
import mx.logging.ILogger;
import mx.logging.Log;
import mx.modules.IModuleInfo;
import mx.modules.ModuleManager;
import mx.resources.ResourceManager;

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

    private var widgetTypeArr:Array = [];
    private var pendingModuleInfos:Array = [];
    private var modulesToLoad:uint;

    public function loadWidgetTypes():void
    {
        if (Log.isInfo())
        {
            LOG.info('Loading modules...');
        }

        var moduleFiles:Array = getModuleFiles(WellKnownDirectories.getInstance().bundledModules)
            .concat(getModuleFiles(WellKnownDirectories.getInstance().customModules));

        //TODO: remove XML files from found SWF files
        modulesToLoad = moduleFiles.length;

        // Load the found modules.
        for each (var moduleFile:File in moduleFiles)
        {
            if (Log.isDebug())
            {
                LOG.debug('loading module {0}', moduleFile.url);
            }

            //we assume XML module files are custom
            if (moduleFile.extension == "xml")
            {
                loadCustomWidgetTypeConfig(moduleFile);
            }
            else if (moduleFile.extension == "swf")
            {
                loadModule(moduleFile);
            }
        }

        checkIfNoMoreModuleInfosLeft();
    }

    private function loadModule(moduleFile:File):void
    {
        var fileBytes:ByteArray = new ByteArray();
        var fileStream:FileStream = new FileStream();
        fileStream.open(moduleFile, FileMode.READ);
        fileStream.readBytes(fileBytes);
        fileStream.close();

        const moduleInfo:IModuleInfo = ModuleManager.getModule(moduleFile.url);
        pendingModuleInfos.push(moduleInfo);
        moduleInfo.addEventListener(ModuleEvent.READY, moduleInfo_readyHandler);
        moduleInfo.addEventListener(ModuleEvent.ERROR, moduleInfo_errorHandler);
        moduleInfo.load(ApplicationDomain.currentDomain, null, fileBytes, FlexGlobals.topLevelApplication.moduleFactory);
    }

    public function loadCustomWidgetTypeConfig(configFile:File):void
    {
        var customWidgetType:CustomWidgetType = createCustomWidgetTypeFromConfig(configFile);
        if (customWidgetType)
        {
            WidgetTypeRegistryModel.getInstance().widgetTypeRegistry.addWidgetType(customWidgetType);
        }

        markModuleAsLoaded();
    }

    private function createCustomWidgetTypeFromConfig(configFile:File):CustomWidgetType
    {
        var fileStream:FileStream = new FileStream();
        var customWidgetType:CustomWidgetType;
        try
        {
            fileStream.open(configFile, FileMode.READ);
            const configXML:XML = XML(fileStream.readUTFBytes(fileStream.bytesAvailable));
            customWidgetType = parseCustomWidgetType(configXML);
        }
        catch (e:Error)
        {
            if (Log.isWarn())
            {
                LOG.warn('Error creating custom module {0}', configFile.nativePath);
            }
        }
        finally
        {
            fileStream.close();
        }

        return customWidgetType;
    }

    private function parseCustomWidgetType(configXML:XML):CustomWidgetType
    {
        var customModule:CustomXMLModule = new CustomXMLModule();
        customModule.widgetName = configXML.name;
        customModule.isOpenByDefault = (configXML.openbydefault == 'true');

        var widgetLabel:String = configXML.label[0];
        var widgetDescription:String = configXML.description[0];
        var widgetHelpURL:String = configXML.helpurl[0];
        var widgetConfiguration:XML = configXML.configuration[0] ? configXML.configuration[0] : <configuration/>;
        var widgetVersion:String = configXML.widgetversion[0];

        customModule.widgetIconLocation = createWidgetIconPath(configXML.icon[0], customModule.widgetName);
        customModule.widgetLabel = widgetLabel ? widgetLabel : customModule.widgetName;
        customModule.widgetDescription = widgetDescription ? widgetDescription : "";
        customModule.widgetHelpURL = widgetHelpURL ? widgetHelpURL : "";

        return new CustomWidgetType(customModule, widgetVersion, widgetConfiguration);
    }

    private function createWidgetIconPath(iconPath:String, widgetName:String):String
    {
        var widgetIconPath:String;

        if (iconPath)
        {
            widgetIconPath = "widgets/" + widgetName + "/" + iconPath;
        }
        else
        {
            widgetIconPath = "assets/images/i_widget.png";
        }

        return widgetIconPath;
    }

    private function getModuleFiles(directory:File):Array
    {
        var moduleFiles:Array = [];

        if (directory.isDirectory)
        {
            const files:Array = directory.getDirectoryListing();

            for each (var file:File in files)
            {
                if(file.isDirectory)
                {
                    moduleFiles = moduleFiles.concat(getModuleFiles(file));
                }
                else if (isModuleFile(file))
                {
                    moduleFiles.push(file);
                }
            }
        }

        return moduleFiles;
    }

    private function isModuleFile(file:File):Boolean
    {
        const moduleFileName:RegExp = /^.*Module\.(swf|xml)$/;
        return !file.isDirectory && (moduleFileName.test(file.name));
    }

    private function moduleInfo_readyHandler(event:ModuleEvent):void
    {
        var moduleInfo:IModuleInfo = event.currentTarget as IModuleInfo;
        moduleInfo.removeEventListener(ModuleEvent.READY, moduleInfo_readyHandler);
        moduleInfo.removeEventListener(ModuleEvent.ERROR, moduleInfo_errorHandler);

        const builderModule:IBuilderModule = event.module.factory.create() as IBuilderModule;
        if (builderModule)
        {
            var customModulesDirectoryURL:String = WellKnownDirectories.getInstance().customModules.url;
            var isCustomModule:Boolean = (moduleInfo.url.indexOf(customModulesDirectoryURL) > -1);
            const widgetType:WidgetType = isCustomModule ?
                new CustomWidgetType(builderModule) :
                new WidgetType(builderModule);
            widgetTypeArr.push(widgetType);

            if (Log.isDebug())
            {
                LOG.debug('Module {0} is resolved', widgetType.name);
            }

        }

        markModuleAsLoaded();
        removeModuleInfo(moduleInfo);
        checkIfNoMoreModuleInfosLeft();
    }

    private function removeModuleInfo(moduleInfo:IModuleInfo):void
    {
        const index:int = pendingModuleInfos.indexOf(moduleInfo);
        if (index > -1)
        {
            pendingModuleInfos.splice(index, 1);
        }
    }

    private function checkIfNoMoreModuleInfosLeft():void
    {
        if (modulesToLoad === 0)
        {
            sortAndAssignWidgetTypes();
        }
    }

    private function markModuleAsLoaded():void
    {
        modulesToLoad--;
    }

    private function sortAndAssignWidgetTypes():void
    {
        if (Log.isInfo())
        {
            LOG.info('All modules resolved');
        }

        widgetTypeArr.sort(compareWidgetTypes);

        var widgetTypes:Array = widgetTypeArr.filter(widgetTypeFilter);
        for each (var widgetType:WidgetType in widgetTypes)
        {
            WidgetTypeRegistryModel.getInstance().widgetTypeRegistry.addWidgetType(widgetType);
        }

        var layoutWidgetTypes:Array = widgetTypeArr.filter(layoutWidgetTypeFilter);
        for each (var layoutWidgetType:WidgetType in layoutWidgetTypes)
        {
            WidgetTypeRegistryModel.getInstance().layoutWidgetTypeRegistry.addWidgetType(layoutWidgetType);
        }

        function widgetTypeFilter(item:WidgetType, index:int, source:Array):Boolean
        {
            return item.isManaged;
        }
        function layoutWidgetTypeFilter(item:WidgetType, index:int, source:Array):Boolean
        {
            return !item.isManaged;
        }

        dispatchEvent(new WidgetTypeLoaderEvent(WidgetTypeLoaderEvent.LOAD_TYPES_COMPLETE));
    }

    private function compareWidgetTypes(lhs:WidgetType, rhs:WidgetType):int
    {
        const lhsLabel:String = lhs.label.toLowerCase();
        const rhsLabel:String = rhs.label.toLowerCase();
        if (lhsLabel < rhsLabel)
        {
            return -1;
        }
        if (lhsLabel > rhsLabel)
        {
            return 1;
        }
        return 0;
    }

    private function moduleInfo_errorHandler(event:ModuleEvent):void
    {
        if (Log.isWarn())
        {
            LOG.warn('Module error: {0}', event.errorText);
        }

        var moduleInfo:IModuleInfo = event.currentTarget as IModuleInfo;
        moduleInfo.removeEventListener(ModuleEvent.ERROR, moduleInfo_errorHandler);
        moduleInfo.removeEventListener(ModuleEvent.READY, moduleInfo_readyHandler);

        Model.instance.status = event.errorText;
        BuilderAlert.show(event.errorText,
                          ResourceManager.getInstance().getString('BuilderStrings', 'error'));
        markModuleAsLoaded();
        removeModuleInfo(moduleInfo);
        checkIfNoMoreModuleInfosLeft();
    }
}
}

Update WidgetTypeLoader#getModuleFiles() to return nested module files.


m4_pattern_allow([^URBI_SERVER$])		         -*- shell-script -*-

AS_INIT()dnl
URBI_PREPARE()

set -e

case $VERBOSE in
  x) set -x;;
esac

# Avoid zombies and preserve debugging information.
cleanup ()
{
  exit_status=$?

  # In case we were caught by set -e, kill the children.
  children_kill
  children_harvest
  children_report

  children_sta=$(children_status remote)
  # Don't clean before calling children_status...
  test x$VERBOSE != x || children_clean

  case $exit_status:$children_sta in
    0:0) ;;
    0:*) # Maybe a children exited for SKIP etc.
	 exit $children_sta;;
    *:*) # If liburbi-check failed, there is a big problem.
	 error SOFTWARE "liburbi-check itself failed with $exit_status";;
  esac
  # rst_expect sets exit=false if it saw a failure.
  $exit
}
for signal in 1 2 13 15; do
  trap 'error $((128 + $signal)) \
	      "received signal $signal ($(kill -l $signal))"' $signal
done
trap cleanup 0

# Overriden to include the test name.
stderr ()
{
  echo >&2 "$(basename $0): $me: $@"
  echo >&2
}

# Sleep some time, but taking into account the fact that
# instrumentation slows down a lot.
my_sleep ()
{
  if $INSTRUMENT; then
    sleep $(($1 * 5))
  else
    sleep $1
  fi
}

## -------------- ##
## Main program.  ##
## -------------- ##

exec 3>&2

# Make it absolute.
chk=$(absolute "$1")
if test ! -f "$chk.cc"; then
  fatal "no such file: $chk.cc"
fi

period=32
# ./../../../tests/2.x/andexp-pipeexp.chk -> 2.x
medir=$(basename $(dirname "$chk"))
# ./../../../tests/2.x/andexp-pipeexp.chk -> 2.x/andexp-pipeexp
me=$medir/$(basename "$chk" ".cc")
# ./../../../tests/2.x/andexp-pipeexp.chk -> andexp-pipeexp
meraw=$(basename $me)    # MERAW!

srcdir=$(absolute $srcdir)
export srcdir
# Move to a private dir.
rm -rf $me.dir
mkdir -p $me.dir
cd $me.dir

# Help debugging
set | rst_pre "$me variables"

# $URBI_SERVER.  Leaves trailing files, so run it in subdir.
find_urbi_server

# compute expected output
sed -n -e 's@//= @@p' $chk.cc >output.exp
touch error.exp
echo 0 >status.exp

#start it
valgrind=$(instrument "server.val")
cmd="$valgrind $URBI_SERVER --port 0 -w server.port --period $period"
echo "$cmd" >server.cmd
$cmd >server.out 2>server.err &
children_register server

my_sleep 2

#start the test
valgrind=$(instrument "remote.val")
cmd="$valgrind ../../tests localhost $(cat server.port) $meraw"
echo "$cmd" >remote.cmd
$cmd >remote.out.raw 2>remote.err &
children_register remote

# Let some time to run the tests.
children_wait 10

# Ignore the "client errors".
sed -e '/^E client error/d' remote.out.raw >remote.out.eff
# Compare expected output with actual output.
rst_expect output remote.out
rst_pre "Error output" remote.err

# Display Valgrind report.
rst_pre "Valgrind" remote.val

# Exit with success: liburbi-check made its job.  But now clean (on
# trap 0) will check $exit to see if there is a failure in the tests
# and adjust the exit status accordingly.
exit 0

m4_pattern_allow([^URBI_SERVER$])		         -*- shell-script -*-

AS_INIT()dnl
URBI_PREPARE()

set -e

case $VERBOSE in
  x) set -x;;
esac

# Avoid zombies and preserve debugging information.
cleanup ()
{
  exit_status=$?

  # In case we were caught by set -e, kill the children.
  children_kill
  children_harvest
  children_report

  children_sta=$(children_status remote)
  # Don't clean before calling children_status...
  test x$VERBOSE != x || children_clean

  case $exit_status:$children_sta in
    0:0) ;;
    0:*) # Maybe a children exited for SKIP etc.
	 exit $children_sta;;
    *:*) # If liburbi-check failed, there is a big problem.
	 error SOFTWARE "liburbi-check itself failed with $exit_status";;
  esac
  # rst_expect sets exit=false if it saw a failure.
  $exit
}
for signal in 1 2 13 15; do
  trap 'error $((128 + $signal)) \
	      "received signal $signal ($(kill -l $signal))"' $signal
done
trap cleanup 0

# Overriden to include the test name.
stderr ()
{
  echo >&2 "$(basename $0): $me: $@"
  echo >&2
}

# Sleep some time, but taking into account the fact that
# instrumentation slows down a lot.
my_sleep ()
{
  if $INSTRUMENT; then
    sleep $(($1 * 5))
  else
    sleep $1
  fi
}

## -------------- ##
## Main program.  ##
## -------------- ##

exec 3>&2

: ${abs_builddir='@abs_builddir@'}
check_dir abs_builddir liburbi-check
: ${abs_top_builddir='@abs_top_builddir@'}
check_dir abs_top_builddir config.status

# Make it absolute.
chk=$(absolute "$1")
if test ! -f "$chk.cc"; then
  fatal "no such file: $chk.cc"
fi

period=32
# ./../../../tests/2.x/andexp-pipeexp.chk -> 2.x
medir=$(basename $(dirname "$chk"))
# ./../../../tests/2.x/andexp-pipeexp.chk -> 2.x/andexp-pipeexp
me=$medir/$(basename "$chk" ".cc")
# ./../../../tests/2.x/andexp-pipeexp.chk -> andexp-pipeexp
meraw=$(basename $me)    # MERAW!

srcdir=$(absolute $srcdir)
export srcdir
# Move to a private dir.
rm -rf $me.dir
mkdir -p $me.dir
cd $me.dir

# Help debugging
set | rst_pre "$me variables"

# $URBI_SERVER.
#
# If this SDK-Remote is part of the Kernel package, then we should not
# use an installed urbi-console, but rather the one which is part of
# this package.
if test -x ${abs_top_builddir}/../src/urbi-console; then
  URBI_SERVER=${abs_top_builddir}/../src/urbi-console
else
  # Leaves trailing files, so run it in subdir.
  find_urbi_server
fi

# compute expected output
sed -n -e 's@//= @@p' $chk.cc >output.exp
touch error.exp
echo 0 >status.exp

#start it
valgrind=$(instrument "server.val")
cmd="$valgrind $URBI_SERVER --port 0 -w server.port --period $period"
echo "$cmd" >server.cmd
$cmd >server.out 2>server.err &
children_register server

my_sleep 2

#start the test
valgrind=$(instrument "remote.val")
cmd="$valgrind ../../tests localhost $(cat server.port) $meraw"
echo "$cmd" >remote.cmd
$cmd >remote.out.raw 2>remote.err &
children_register remote

# Let some time to run the tests.
children_wait 10

# Ignore the "client errors".
sed -e '/^E client error/d' remote.out.raw >remote.out.eff
# Compare expected output with actual output.
rst_expect output remote.out
rst_pre "Error output" remote.err

# Display Valgrind report.
rst_pre "Valgrind" remote.val

# Exit with success: liburbi-check made its job.  But now clean (on
# trap 0) will check $exit to see if there is a failure in the tests
# and adjust the exit status accordingly.
exit 0

Use the local urbi-console.

Use the local urbi-console.

Alternatively, I could change PATH and let find_urbi_server do the
job.  Not sure what's the easiest.  This way we can have a specific
failure when urbi-console is not there but it should.

	* src/tests/liburbi-check.as ($URBI_SERVER): Find the shipped
        urbi-console if there is one.


package aerys.minko.scene.controller.light
{
	import aerys.minko.scene.data.LightDataProvider;
	import aerys.minko.scene.node.Scene;
	import aerys.minko.scene.node.light.AbstractLight;
	import aerys.minko.scene.node.light.DirectionalLight;
	import aerys.minko.type.math.Matrix4x4;
	import aerys.minko.type.math.Vector4;

	/**
	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public final class DirectionalLightController extends LightController
	{
		private static const SCREEN_TO_UV	: Matrix4x4	= new Matrix4x4(
			.5,		.0,		.0,		.0,
			.0, 	-.5,	.0,		.0,
			.0,		.0,		1.,		.0,
			.5, 	.5,		.0,		1.
		);

		private var _worldPosition		: Vector4;
		private var _worldDirection		: Vector4;
		private var _worldToScreen		: Matrix4x4;
		private var _worldToUV			: Matrix4x4;
		private var _projection			: Matrix4x4;

		public function DirectionalLightController()
		{
			super(DirectionalLight);

			initialize();
		}

		private function initialize() : void
		{
			_worldPosition = new Vector4();
			_worldDirection = new Vector4();
			_worldToScreen = new Matrix4x4();
			_worldToUV = new Matrix4x4();
			_projection = new Matrix4x4();
		}

		override protected function lightAddedHandler(ctrl		: LightController,
													  target	: AbstractLight) : void
		{
			super.lightAddedHandler(ctrl, target);

			lightData.setLightProperty('worldPosition', _worldPosition);
			lightData.setLightProperty('worldDirection', _worldDirection);
			lightData.setLightProperty('worldToScreen', _worldToScreen);
			lightData.setLightProperty('worldToUV', _worldToUV);
			lightData.setLightProperty('projection', _projection);
		}

		override protected function lightAddedToSceneHandler(light 	: AbstractLight,
															 scene	: Scene) : void
		{
			super.lightAddedToSceneHandler(light, scene);

			updateProjectionMatrix();
			lightLocalToWorldChangedHandler(light.localToWorld);
			light.localToWorld.changed.add(lightLocalToWorldChangedHandler);
		}

		override protected function lightRemovedFromSceneHandler(light	: AbstractLight,
																 scene	: Scene) : void
		{
			super.lightRemovedFromSceneHandler(light, scene);

			light.localToWorld.changed.remove(lightLocalToWorldChangedHandler);
		}

		override protected function lightDataChangedHandler(lightData		: LightDataProvider,
															propertyName	: String) : void
		{
			super.lightDataChangedHandler(lightData, propertyName);

			propertyName = LightDataProvider.getPropertyName(propertyName);

			if (propertyName == 'shadowWidth' || propertyName == 'shadowMaxZ')
				updateProjectionMatrix();
		}

		private function lightLocalToWorldChangedHandler(localToWorld : Matrix4x4) : void
		{
			// compute position
			localToWorld.getTranslation(_worldPosition);

			// compute direction
			_worldDirection	= localToWorld.deltaTransformVector(Vector4.Z_AXIS, _worldDirection);
			_worldDirection.normalize();

			// update world to screen/uv
			_worldToScreen.lock().copyFrom(light.worldToLocal).append(_projection).unlock();
			_worldToUV.lock().copyFrom(_worldToScreen).append(SCREEN_TO_UV).unlock();
		}

		private function updateProjectionMatrix() : void
		{
			var zFar 	: Number 	= lightData.getLightProperty('shadowMaxZ');
			var width	: Number	= lightData.getLightProperty('shadowWidth');

			_projection.initialize(
				2. / width, 	0., 			0.,			0.,
				0., 			2. / width, 	0.,			0.,
				0., 			0., 			2. / zFar, 	0.,
				0., 			0., 			0.,			1.
			);

			_worldToScreen.lock().copyFrom(light.worldToLocal).append(_projection).unlock();
			_worldToUV.lock().copyFrom(_worldToScreen).append(SCREEN_TO_UV).unlock();
		}
	}
}

package aerys.minko.scene.controller.light
{
	import aerys.minko.scene.data.LightDataProvider;
	import aerys.minko.scene.node.Scene;
	import aerys.minko.scene.node.light.AbstractLight;
	import aerys.minko.scene.node.light.DirectionalLight;
	import aerys.minko.type.math.Matrix4x4;
	import aerys.minko.type.math.Vector4;

	/**
	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public final class DirectionalLightController extends LightController
	{
		private static const SCREEN_TO_UV	: Matrix4x4	= new Matrix4x4(
			.5,		.0,		.0,		.0,
			.0, 	-.5,	.0,		.0,
			.0,		.0,		1.,		.0,
			.5, 	.5,		.0,		1.
		);

		private var _worldPosition		: Vector4;
		private var _worldDirection		: Vector4;
		private var _worldToScreen		: Matrix4x4;
		private var _worldToUV			: Matrix4x4;
		private var _projection			: Matrix4x4;

		public function DirectionalLightController()
		{
			super(DirectionalLight);

			initialize();
		}

		private function initialize() : void
		{
			_worldPosition = new Vector4();
			_worldDirection = new Vector4();
			_worldToScreen = new Matrix4x4();
			_worldToUV = new Matrix4x4();
			_projection = new Matrix4x4();
		}

		override protected function lightAddedHandler(ctrl		: LightController,
													  target	: AbstractLight) : void
		{
			super.lightAddedHandler(ctrl, target);

			lightData.setLightProperty('worldPosition', _worldPosition);
			lightData.setLightProperty('worldDirection', _worldDirection);
			lightData.setLightProperty('worldToScreen', _worldToScreen);
			lightData.setLightProperty('worldToUV', _worldToUV);
			lightData.setLightProperty('projection', _projection);
		}

		override protected function lightAddedToSceneHandler(light 	: AbstractLight,
															 scene	: Scene) : void
		{
			super.lightAddedToSceneHandler(light, scene);

			updateProjectionMatrix();
			lightLocalToWorldChangedHandler(light.localToWorld);
			light.localToWorld.changed.add(lightLocalToWorldChangedHandler);
		}

		override protected function lightRemovedFromSceneHandler(light	: AbstractLight,
																 scene	: Scene) : void
		{
			super.lightRemovedFromSceneHandler(light, scene);

			light.localToWorld.changed.remove(lightLocalToWorldChangedHandler);
		}

		override protected function lightDataChangedHandler(lightData		: LightDataProvider,
															propertyName	: String) : void
		{
			super.lightDataChangedHandler(lightData, propertyName);

			propertyName = LightDataProvider.getPropertyName(propertyName);

			if (propertyName == 'shadowWidth' || propertyName == 'shadowZFar')
				updateProjectionMatrix();
		}

		private function lightLocalToWorldChangedHandler(localToWorld : Matrix4x4) : void
		{
			// compute position
			localToWorld.getTranslation(_worldPosition);

			// compute direction
			_worldDirection	= localToWorld.deltaTransformVector(Vector4.Z_AXIS, _worldDirection);
			_worldDirection.normalize();

			// update world to screen/uv
			_worldToScreen.lock().copyFrom(light.worldToLocal).append(_projection).unlock();
			_worldToUV.lock().copyFrom(_worldToScreen).append(SCREEN_TO_UV).unlock();
		}

		private function updateProjectionMatrix() : void
		{
			var zFar 	: Number 	= lightData.getLightProperty('shadowZFar');
			var width	: Number	= lightData.getLightProperty('shadowWidth');

			_projection.initialize(
				2. / width, 	0., 			0.,			0.,
				0., 			2. / width, 	0.,			0.,
				0., 			0., 			2. / zFar, 	0.,
				0., 			0., 			0.,			1.
			);

			_worldToScreen.lock().copyFrom(light.worldToLocal).append(_projection).unlock();
			_worldToUV.lock().copyFrom(_worldToScreen).append(SCREEN_TO_UV).unlock();
		}
	}
}

fix wrong reference to the 'shadowMaxZ' property