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

import com.esri.builder.eventbus.AppEvent;
import com.esri.builder.model.LocaleModel;
import com.esri.builder.model.Model;
import com.esri.builder.model.WidgetTypeRegistryModel;
import com.esri.builder.supportClasses.LogUtil;

import mx.core.FlexGlobals;
import mx.logging.ILogger;
import mx.logging.Log;
import mx.resources.ResourceManager;
import mx.styles.CSSStyleDeclaration;
import mx.styles.IStyleManager2;
import mx.styles.StyleManager;

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

    public function LocaleController()
    {
        AppEvent.addListener(AppEvent.SETTINGS_SAVED, settingsChangeHandler, false, 500); //high priority to prevent app using outdated locale settings
    }

    private function settingsChangeHandler(event:AppEvent):void
    {
        updateApplicationLocale();
    }

    private function updateApplicationLocale():void
    {
        var selectedLocale:String = Model.instance.locale;
        var localeChain:Array = ResourceManager.getInstance().localeChain;

        var selectedLocaleIndex:int = localeChain.indexOf(selectedLocale);
        localeChain.splice(selectedLocaleIndex, 1);
        localeChain.unshift(selectedLocale);

        if (Log.isDebug())
        {
            LOG.debug("Updating application locale: {0}", selectedLocale);
        }

        ResourceManager.getInstance().update();
        applyLocaleLayoutDirection(selectedLocale);
        setPreferredLocaleFonts(selectedLocale);
        setLocaleSpecificStyles(selectedLocale);
    }

    private function applyLocaleLayoutDirection(selectedLocale:String):void
    {
        var currentLayoutDirection:String = FlexGlobals.topLevelApplication.getStyle('layoutDirection');
        var localeLayoutDirection:String = LocaleModel.getInstance().getLocaleLayoutDirection(selectedLocale);
        if (localeLayoutDirection != currentLayoutDirection)
        {
            FlexGlobals.topLevelApplication.setStyle('direction', localeLayoutDirection);
            FlexGlobals.topLevelApplication.setStyle('layoutDirection', localeLayoutDirection);
        }

        var currentLocale:String = FlexGlobals.topLevelApplication.getStyle('locale');
        if (selectedLocale != currentLocale)
        {
            FlexGlobals.topLevelApplication.setStyle('locale', selectedLocale);
            WidgetTypeRegistryModel.getInstance().widgetTypeRegistry.sort();
        }
    }

    private function setPreferredLocaleFonts(selectedLocale:String):void
    {
        if (selectedLocale == 'ar')
        {
            FlexGlobals.topLevelApplication.setStyle('fontFamily', toFontFamily(LocaleModel.PREFERRED_ARABIC_FONTS));
        }
        else if (selectedLocale == 'ja_JP')
        {
            FlexGlobals.topLevelApplication.setStyle('fontFamily', toFontFamily(LocaleModel.PREFERRED_JAPANESE_FONTS));
        }
        else if (selectedLocale == 'ko_KR')
        {
            FlexGlobals.topLevelApplication.setStyle('fontFamily', toFontFamily(LocaleModel.PREFERRED_KOREAN_FONTS));
        }
        else if (selectedLocale == 'zh_CN')
        {
            FlexGlobals.topLevelApplication.setStyle('fontFamily', toFontFamily(LocaleModel.PREFERRED_CHINESE_FONTS));
        }
        else
        {
            FlexGlobals.topLevelApplication.setStyle('fontFamily', undefined);
        }
    }

    private function toFontFamily(fontNames:Array):String
    {
        return fontNames ? fontNames.join(',') : "";
    }

    private function setLocaleSpecificStyles(locale:String):void
    {
        var topLevelStyleManager:IStyleManager2 = StyleManager.getStyleManager(null);

        var isItalicInappropriateLocale:Boolean =
            locale == 'ja_JP' || locale == 'ko_KR' || locale == 'zh_CN'
            || locale == 'ar' || locale == 'he_IL';

        var emphasisStyle:CSSStyleDeclaration = topLevelStyleManager.getStyleDeclaration(".emphasisText");
        var emphasisFontStyle:String = isItalicInappropriateLocale ? "normal" : "italic";
        emphasisStyle.setStyle("fontStyle", emphasisFontStyle);

        var isBoldInappropriateLocale:Boolean =
            locale == 'ja_JP' || locale == 'ko_KR' || locale == 'zh_CN';

        var boldStyle:CSSStyleDeclaration = topLevelStyleManager.getStyleDeclaration(".boldText");
        var emphasisFontWeight:String = isBoldInappropriateLocale ? "normal" : "bold";
        boldStyle.setStyle("fontWeight", emphasisFontWeight);
    }
}
}

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

import com.esri.builder.eventbus.AppEvent;
import com.esri.builder.model.LocaleModel;
import com.esri.builder.model.Model;
import com.esri.builder.model.WidgetTypeRegistryModel;
import com.esri.builder.supportClasses.LogUtil;

import mx.core.FlexGlobals;
import mx.logging.ILogger;
import mx.logging.Log;
import mx.resources.ResourceManager;
import mx.styles.CSSStyleDeclaration;
import mx.styles.IStyleManager2;
import mx.styles.StyleManager;

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

    public function LocaleController()
    {
        AppEvent.addListener(AppEvent.SETTINGS_SAVED, settingsChangeHandler, false, 500); //high priority to prevent app using outdated locale settings
    }

    private function settingsChangeHandler(event:AppEvent):void
    {
        updateApplicationLocale();
    }

    private function updateApplicationLocale():void
    {
        var selectedLocale:String = Model.instance.locale;
        var localeChain:Array = ResourceManager.getInstance().localeChain;

        var selectedLocaleIndex:int = localeChain.indexOf(selectedLocale);
        localeChain.splice(selectedLocaleIndex, 1);
        localeChain.unshift(selectedLocale);

        if (Log.isDebug())
        {
            LOG.debug("Updating application locale: {0}", selectedLocale);
        }

        ResourceManager.getInstance().update();
        applyLocaleLayoutDirection(selectedLocale);
        setPreferredLocaleFonts(selectedLocale);
        setLocaleSpecificStyles(selectedLocale);
    }

    private function applyLocaleLayoutDirection(selectedLocale:String):void
    {
        var currentLayoutDirection:String = FlexGlobals.topLevelApplication.getStyle('layoutDirection');
        var localeLayoutDirection:String = LocaleModel.getInstance().getLocaleLayoutDirection(selectedLocale);
        if (localeLayoutDirection != currentLayoutDirection)
        {
            FlexGlobals.topLevelApplication.setStyle('direction', localeLayoutDirection);
            FlexGlobals.topLevelApplication.setStyle('layoutDirection', localeLayoutDirection);
        }

        var currentLocale:String = FlexGlobals.topLevelApplication.getStyle('locale');
        if (selectedLocale != currentLocale)
        {
            FlexGlobals.topLevelApplication.setStyle('locale', selectedLocale);
            WidgetTypeRegistryModel.getInstance().widgetTypeRegistry.sort();
        }
    }

    private function setPreferredLocaleFonts(selectedLocale:String):void
    {
        if (selectedLocale == 'ar')
        {
            FlexGlobals.topLevelApplication.setStyle('fontFamily', toFontFamily(LocaleModel.PREFERRED_ARABIC_FONTS));
        }
        else if (selectedLocale == 'ja_JP')
        {
            FlexGlobals.topLevelApplication.setStyle('fontFamily', toFontFamily(LocaleModel.PREFERRED_JAPANESE_FONTS));
        }
        else if (selectedLocale == 'ko_KR')
        {
            FlexGlobals.topLevelApplication.setStyle('fontFamily', toFontFamily(LocaleModel.PREFERRED_KOREAN_FONTS));
        }
        else if (selectedLocale == 'zh_CN')
        {
            FlexGlobals.topLevelApplication.setStyle('fontFamily', toFontFamily(LocaleModel.PREFERRED_CHINESE_FONTS));
        }
        else
        {
            FlexGlobals.topLevelApplication.setStyle('fontFamily', undefined);
        }
    }

    private function toFontFamily(fontNames:Array):String
    {
        if (!fontNames)
        {
            return "";
        }

        var fallbackFontName:String = "_sans";
        fontNames.push(fallbackFontName);

        return fontNames.join(',');
    }

    private function setLocaleSpecificStyles(locale:String):void
    {
        var topLevelStyleManager:IStyleManager2 = StyleManager.getStyleManager(null);

        var isItalicInappropriateLocale:Boolean =
            locale == 'ja_JP' || locale == 'ko_KR' || locale == 'zh_CN'
            || locale == 'ar' || locale == 'he_IL';

        var emphasisStyle:CSSStyleDeclaration = topLevelStyleManager.getStyleDeclaration(".emphasisText");
        var emphasisFontStyle:String = isItalicInappropriateLocale ? "normal" : "italic";
        emphasisStyle.setStyle("fontStyle", emphasisFontStyle);

        var isBoldInappropriateLocale:Boolean =
            locale == 'ja_JP' || locale == 'ko_KR' || locale == 'zh_CN';

        var boldStyle:CSSStyleDeclaration = topLevelStyleManager.getStyleDeclaration(".boldText");
        var emphasisFontWeight:String = isBoldInappropriateLocale ? "normal" : "bold";
        boldStyle.setStyle("fontWeight", emphasisFontWeight);
    }
}
}

Add fallback font name for locale-specific font families.


package dolly {
import dolly.core.dolly_internal;

import org.as3commons.reflect.Field;
import org.as3commons.reflect.Type;
import org.flexunit.asserts.assertEquals;
import org.flexunit.asserts.assertNotNull;
import org.flexunit.asserts.assertNull;

use namespace dolly_internal;

public class ClonerTest {

	private var classMarkedAsCloneable:ClassLevelCloneable;
	private var classWithSomeCloneableFields:PropertyLevelCloneable;
	private var classMarkedAsCloneableType:Type;
	private var classWithSomeCloneableFieldsType:Type;

	[Before]
	public function before():void {
		classMarkedAsCloneable = new ClassLevelCloneable();
		classMarkedAsCloneable.property1 = "value 1";
		classMarkedAsCloneable.property2 = "value 2";
		classMarkedAsCloneable.property3 = "value 3";
		classMarkedAsCloneable.writableField = "value 4";

		classWithSomeCloneableFields = new PropertyLevelCloneable();
		classWithSomeCloneableFields.property1 = "value 1";
		classWithSomeCloneableFields.property2 = "value 2";
		classWithSomeCloneableFields.property3 = "value 3";
		classWithSomeCloneableFields.writableField = "value 4";

		classMarkedAsCloneableType = Type.forInstance(classMarkedAsCloneable);
		classWithSomeCloneableFieldsType = Type.forInstance(classWithSomeCloneableFields);
	}

	[After]
	public function after():void {
		classMarkedAsCloneable = null;
		classWithSomeCloneableFields = null;
		classMarkedAsCloneableType = null;
		classWithSomeCloneableFieldsType = null;
	}

	[Test]
	public function testGetCloneableFieldsForType():void {
		var cloneableFields:Vector.<Field> = Cloner.getCloneableFieldsForType(classMarkedAsCloneableType);

		assertNotNull(cloneableFields);
		assertEquals(4, cloneableFields.length);

		cloneableFields = Cloner.getCloneableFieldsForType(classWithSomeCloneableFieldsType);

		assertNotNull(cloneableFields);
		assertEquals(3, cloneableFields.length);
	}

	[Test]
	public function testCloneWithClassLevelMetadata():void {
		const clone1:ClassLevelCloneable = Cloner.clone(classMarkedAsCloneable) as ClassLevelCloneable;

		assertNotNull(clone1);
		assertNotNull(clone1.property1);
		assertEquals(clone1.property1, classMarkedAsCloneable.property1);
		assertNotNull(clone1.property2);
		assertEquals(clone1.property2, classMarkedAsCloneable.property2);
		assertNotNull(clone1.property3);
		assertEquals(clone1.property3, classMarkedAsCloneable.property3);
		assertNotNull(clone1.writableField);
		assertEquals(clone1.writableField, classMarkedAsCloneable.writableField);
	}

	[Test]
	public function testCloneWithPropertyLevelMetadata():void {
		const clone2:PropertyLevelCloneable = Cloner.clone(
				classWithSomeCloneableFields
		) as PropertyLevelCloneable;

		assertNotNull(clone2);
		assertNull(clone2.property1);
		assertNotNull(clone2.property2);
		assertEquals(clone2.property2, classWithSomeCloneableFields.property2);
		assertNotNull(clone2.property3);
		assertEquals(clone2.property3, classWithSomeCloneableFields.property3);
		assertNotNull(clone2.writableField);
		assertEquals(clone2.writableField, classWithSomeCloneableFields.writableField);
	}
}
}

package dolly {
import dolly.core.dolly_internal;

import org.as3commons.reflect.Field;
import org.as3commons.reflect.Type;
import org.flexunit.asserts.assertEquals;
import org.flexunit.asserts.assertNotNull;
import org.flexunit.asserts.assertNull;

use namespace dolly_internal;

public class ClonerTest {

	private var classMarkedAsCloneable:ClassLevelCloneable;
	private var classWithSomeCloneableFields:PropertyLevelCloneable;
	private var classMarkedAsCloneableType:Type;
	private var classWithSomeCloneableFieldsType:Type;

	[Before]
	public function before():void {
		classMarkedAsCloneable = new ClassLevelCloneable();
		classMarkedAsCloneable.property1 = "value 1";
		classMarkedAsCloneable.property2 = "value 2";
		classMarkedAsCloneable.property3 = "value 3";
		classMarkedAsCloneable.writableField = "value 4";

		classWithSomeCloneableFields = new PropertyLevelCloneable();
		classWithSomeCloneableFields.property1 = "value 1";
		classWithSomeCloneableFields.property2 = "value 2";
		classWithSomeCloneableFields.property3 = "value 3";
		classWithSomeCloneableFields.writableField = "value 4";

		classMarkedAsCloneableType = Type.forInstance(classMarkedAsCloneable);
		classWithSomeCloneableFieldsType = Type.forInstance(classWithSomeCloneableFields);
	}

	[After]
	public function after():void {
		classMarkedAsCloneable = null;
		classWithSomeCloneableFields = null;
		classMarkedAsCloneableType = null;
		classWithSomeCloneableFieldsType = null;
	}

	[Test]
	public function testGetCloneableFieldsForType():void {
		var cloneableFields:Vector.<Field> = Cloner.getCloneableFieldsForType(classMarkedAsCloneableType);

		assertNotNull(cloneableFields);
		assertEquals(4, cloneableFields.length);

		cloneableFields = Cloner.getCloneableFieldsForType(classWithSomeCloneableFieldsType);

		assertNotNull(cloneableFields);
		assertEquals(3, cloneableFields.length);
	}

	[Test]
	public function testCloneWithClassLevelMetadata():void {
		const clone:ClassLevelCloneable = Cloner.clone(classMarkedAsCloneable) as ClassLevelCloneable;

		assertNotNull(clone);
		assertNotNull(clone.property1);
		assertEquals(clone.property1, classMarkedAsCloneable.property1);
		assertNotNull(clone.property2);
		assertEquals(clone.property2, classMarkedAsCloneable.property2);
		assertNotNull(clone.property3);
		assertEquals(clone.property3, classMarkedAsCloneable.property3);
		assertNotNull(clone.writableField);
		assertEquals(clone.writableField, classMarkedAsCloneable.writableField);
	}

	[Test]
	public function testCloneWithPropertyLevelMetadata():void {
		const clone:PropertyLevelCloneable = Cloner.clone(
				classWithSomeCloneableFields
		) as PropertyLevelCloneable;

		assertNotNull(clone);
		assertNull(clone.property1);
		assertNotNull(clone.property2);
		assertEquals(clone.property2, classWithSomeCloneableFields.property2);
		assertNotNull(clone.property3);
		assertEquals(clone.property3, classWithSomeCloneableFields.property3);
		assertNotNull(clone.writableField);
		assertEquals(clone.writableField, classWithSomeCloneableFields.writableField);
	}
}
}

Rename local variables.

Rename local variables.


package aerys.minko.scene.node
{
	import aerys.minko.ns.minko_scene;
	import aerys.minko.render.geometry.Geometry;
	import aerys.minko.render.material.Material;
	import aerys.minko.render.material.basic.BasicMaterial;
	import aerys.minko.scene.controller.mesh.MeshController;
	import aerys.minko.scene.controller.mesh.MeshVisibilityController;
	import aerys.minko.scene.data.TransformDataProvider;
	import aerys.minko.type.Signal;
	import aerys.minko.type.binding.DataBindings;
	import aerys.minko.type.binding.DataProvider;
	import aerys.minko.type.enum.DataProviderUsage;
	import aerys.minko.type.enum.FrustumCulling;
	import aerys.minko.type.math.Ray;

	use namespace minko_scene;

	/**
	 * Mesh objects are a visible instance of a Geometry rendered using a specific
	 * Effect with specific rendering properties.
	 *
	 *

	 * Those rendering properties are stored in a DataBindings object so they can
	 * be directly used by the shaders in the rendering API.
	 *

	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public class Mesh extends AbstractVisibleSceneNode
	{
		public static const DEFAULT_MATERIAL	: Material	= new BasicMaterial();

		private var _geometry			: Geometry;
		private var _properties			: DataProvider;
		private var _material			: Material;
		private var _bindings			: DataBindings;

		private var _visibility			: MeshVisibilityController;

		private var _frame				: uint;

		private var _cloned				: Signal;
		private var _materialChanged	: Signal;
		private var _frameChanged		: Signal;
		private var _geometryChanged	: Signal;

		/**
		 * A DataProvider object already bound to the Mesh bindings.
		 *
		 *

		 * // set the "diffuseColor" property to 0x0000ffff
		 * mesh.properties.diffuseColor = 0x0000ffff;
		 *
		 * // animate the "diffuseColor" property
		 * mesh.addController(
		 *   new AnimationController(
		 * 	  new <ITimeline>[new ColorTimeline(
		 * 	    "dataProvider.diffuseColor",
		 * 	    5000,
		 * 	    new <uint>[0xffffffff, 0xffffff00, 0xffffffff]
		 *    )]
		 *   )
		 * );
		 *

		 *
		 * @return
		 *
		 */
		public function get properties() : DataProvider
		{
			return _properties;
		}

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

				_properties = value;

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

		public function get material() : Material
		{
			return _material;
		}
		public function set material(value : Material) : void
		{
			if (_material != value)
			{
				if (_material)
					_bindings.removeProvider(_material);

				var oldMaterial : Material = _material;

				_material = value;

				if (value)
					_bindings.addProvider(value);

				_materialChanged.execute(this, oldMaterial, value);
			}
		}

		/**
		 * The rendering properties provided to the shaders to customize
		 * how the mesh will appear on screen.
		 *
		 * @return
		 *
		 */
		public function get bindings() : DataBindings
		{
			return _bindings;
		}

		/**
		 * The Geometry of the mesh.
		 * @return
		 *
		 */
		public function get geometry() : Geometry
		{
			return _geometry;
		}

		public function set geometry(value : Geometry) : void
		{
			if (_geometry != value)
			{
				var oldGeometry	: Geometry = _geometry;

				if (oldGeometry)
					oldGeometry.changed.remove(geometryChangedHandler);

				_geometry = value;

				if (value)
					_geometry.changed.add(geometryChangedHandler);

				_geometryChanged.execute(this, oldGeometry, value);
			}
		}

        /**
         * Whether the mesh in inside the camera frustum or not.
         * @return
         *
         */
        public function get insideFrustum() : Boolean
        {
            return _visibility.insideFrustum;
        }

        override public function get computedVisibility() : Boolean
        {
            return scene
				? super.computedVisibility && _visibility.computedVisibility
				: super.computedVisibility;
        }

		public function get frustumCulling() : uint
		{
			return _visibility.frustumCulling;
		}
		public function set frustumCulling(value : uint) : void
		{
			_visibility.frustumCulling = value;
		}

		public function get cloned() : Signal
		{
			return _cloned;
		}

		public function get materialChanged() : Signal
		{
			return _materialChanged;
		}

		public function get frameChanged() : Signal
		{
			return _frameChanged;
		}

		public function get geometryChanged() : Signal
		{
			return _geometryChanged;
		}

		public function get frame() : uint
		{
			return _frame;
		}
		public function set frame(value : uint) : void
		{
			if (_frame != value)
			{
				var oldFrame : uint = _frame;

				_frame = value;
				_frameChanged.execute(this, oldFrame, value);
			}
		}

		public function Mesh(geometry	: Geometry	= null,
							 material	: Material	= null,
							 name		: String	= null)
		{
			super();

			initializeMesh(geometry, material, name);
		}

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

			super.initialize();
		}

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

			_cloned = new Signal('Mesh.cloned');
			_materialChanged = new Signal('Mesh.materialChanged');
			_frameChanged = new Signal('Mesh.frameChanged');
			_geometryChanged = new Signal('Mesh.geometryChanged');
		}

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

			addController(new MeshController());

			_visibility = new MeshVisibilityController();
			_visibility.frustumCulling = FrustumCulling.ENABLED;
			addController(_visibility);
		}

		protected function initializeMesh(geometry	: Geometry,
										  material	: Material,
										  name		: String) : void
		{
			if (name)
				this.name = name;

			this.geometry = geometry;
			this.material = material || DEFAULT_MATERIAL;
		}

		public function cast(ray : Ray, maxDistance : Number = Number.POSITIVE_INFINITY) : Number
		{
			return _geometry.boundingBox.testRay(
				ray,
				getWorldToLocalTransform(),
				maxDistance
			);
		}

		override minko_scene function cloneNode() : AbstractSceneNode
		{
			var clone : Mesh = new Mesh();

			clone.name 		= name;
			clone.geometry 	= _geometry;

			clone.properties = DataProvider(_properties.clone());
			clone._bindings.copySharedProvidersFrom(_bindings);

			clone.transform.copyFrom(transform);
			clone.material = _material;

			this.cloned.execute(this, clone);

			return clone;
		}

		private function geometryChangedHandler(geometry : Geometry) : void
		{
			_geometryChanged.execute(this, _geometry, _geometry);
		}
	}
}

package aerys.minko.scene.node
{
	import aerys.minko.ns.minko_scene;
	import aerys.minko.render.geometry.Geometry;
	import aerys.minko.render.material.Material;
	import aerys.minko.render.material.basic.BasicMaterial;
	import aerys.minko.scene.controller.mesh.MeshController;
	import aerys.minko.scene.controller.mesh.MeshVisibilityController;
	import aerys.minko.scene.data.TransformDataProvider;
	import aerys.minko.type.Signal;
	import aerys.minko.type.binding.DataBindings;
	import aerys.minko.type.binding.DataProvider;
	import aerys.minko.type.enum.DataProviderUsage;
	import aerys.minko.type.enum.FrustumCulling;
	import aerys.minko.type.math.Ray;

	use namespace minko_scene;

	/**
	 * Mesh objects are a visible instance of a Geometry rendered using a specific
	 * Effect with specific rendering properties.
	 *
	 *

	 * Those rendering properties are stored in a DataBindings object so they can
	 * be directly used by the shaders in the rendering API.
	 *

	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public class Mesh extends AbstractVisibleSceneNode
	{
		public static const DEFAULT_MATERIAL	: Material	= new BasicMaterial();

		private var _geometry			: Geometry;
		private var _properties			: DataProvider;
		private var _material			: Material;
		private var _bindings			: DataBindings;

		private var _visibility			: MeshVisibilityController;

		private var _frame				: uint;

		private var _cloned				: Signal;
		private var _materialChanged	: Signal;
		private var _frameChanged		: Signal;
		private var _geometryChanged	: Signal;

		/**
		 * A DataProvider object already bound to the Mesh bindings.
		 *
		 *

		 * // set the "diffuseColor" property to 0x0000ffff
		 * mesh.properties.diffuseColor = 0x0000ffff;
		 *
		 * // animate the "diffuseColor" property
		 * mesh.addController(
		 *   new AnimationController(
		 * 	  new <ITimeline>[new ColorTimeline(
		 * 	    "dataProvider.diffuseColor",
		 * 	    5000,
		 * 	    new <uint>[0xffffffff, 0xffffff00, 0xffffffff]
		 *    )]
		 *   )
		 * );
		 *

		 *
		 * @return
		 *
		 */
		public function get properties() : DataProvider
		{
			return _properties;
		}

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

				_properties = value;

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

		public function get material() : Material
		{
			return _material;
		}
		public function set material(value : Material) : void
		{
			if (_material != value)
			{
				if (_material)
					_bindings.removeProvider(_material);

				var oldMaterial : Material = _material;

				_material = value;

				if (value)
					_bindings.addProvider(value);

				_materialChanged.execute(this, oldMaterial, value);
			}
		}

		/**
		 * The rendering properties provided to the shaders to customize
		 * how the mesh will appear on screen.
		 *
		 * @return
		 *
		 */
		public function get bindings() : DataBindings
		{
			return _bindings;
		}

		/**
		 * The Geometry of the mesh.
		 * @return
		 *
		 */
		public function get geometry() : Geometry
		{
			return _geometry;
		}

		public function set geometry(value : Geometry) : void
		{
			if (_geometry != value)
			{
				var oldGeometry	: Geometry = _geometry;

				if (oldGeometry)
					oldGeometry.changed.remove(geometryChangedHandler);

				_geometry = value;

				if (value)
					_geometry.changed.add(geometryChangedHandler);

				_geometryChanged.execute(this, oldGeometry, value);
			}
		}

        /**
         * Whether the mesh in inside the camera frustum or not.
         * @return
         *
         */
        public function get insideFrustum() : Boolean
        {
            return _visibility.insideFrustum;
        }

        override public function get computedVisibility() : Boolean
        {
			var visibility : Boolean = super.computedVisibility && _material && _material.effect;

            return scene ? visibility && _visibility.computedVisibility : visibility;
        }

		public function get frustumCulling() : uint
		{
			return _visibility.frustumCulling;
		}
		public function set frustumCulling(value : uint) : void
		{
			_visibility.frustumCulling = value;
		}

		public function get cloned() : Signal
		{
			return _cloned;
		}

		public function get materialChanged() : Signal
		{
			return _materialChanged;
		}

		public function get frameChanged() : Signal
		{
			return _frameChanged;
		}

		public function get geometryChanged() : Signal
		{
			return _geometryChanged;
		}

		public function get frame() : uint
		{
			return _frame;
		}
		public function set frame(value : uint) : void
		{
			if (_frame != value)
			{
				var oldFrame : uint = _frame;

				_frame = value;
				_frameChanged.execute(this, oldFrame, value);
			}
		}

		public function Mesh(geometry	: Geometry	= null,
							 material	: Material	= null,
							 name		: String	= null)
		{
			super();

			initializeMesh(geometry, material, name);
		}

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

			super.initialize();
		}

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

			_cloned = new Signal('Mesh.cloned');
			_materialChanged = new Signal('Mesh.materialChanged');
			_frameChanged = new Signal('Mesh.frameChanged');
			_geometryChanged = new Signal('Mesh.geometryChanged');
		}

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

			addController(new MeshController());

			_visibility = new MeshVisibilityController();
			_visibility.frustumCulling = FrustumCulling.ENABLED;
			addController(_visibility);
		}

		protected function initializeMesh(geometry	: Geometry,
										  material	: Material,
										  name		: String) : void
		{
			if (name)
				this.name = name;

			this.geometry = geometry;
			this.material = material || DEFAULT_MATERIAL;
		}

		public function cast(ray : Ray, maxDistance : Number = Number.POSITIVE_INFINITY) : Number
		{
			return _geometry.boundingBox.testRay(
				ray,
				getWorldToLocalTransform(),
				maxDistance
			);
		}

		override minko_scene function cloneNode() : AbstractSceneNode
		{
			var clone : Mesh = new Mesh();

			clone.name 		= name;
			clone.geometry 	= _geometry;

			clone.properties = DataProvider(_properties.clone());
			clone._bindings.copySharedProvidersFrom(_bindings);

			clone.transform.copyFrom(transform);
			clone.material = _material;

			this.cloned.execute(this, clone);

			return clone;
		}

		private function geometryChangedHandler(geometry : Geometry) : void
		{
			_geometryChanged.execute(this, _geometry, _geometry);
		}
	}
}

fix Mesh.computedVisibility to take material and material.effect availability into account: mesh without material/effect are now considered as hidden


package dolly.utils {
import dolly.core.dolly_internal;

import mx.collections.ArrayCollection;
import mx.collections.ArrayList;

import org.flexunit.assertThat;
import org.flexunit.asserts.assertTrue;
import org.hamcrest.collection.array;
import org.hamcrest.collection.arrayWithSize;
import org.hamcrest.collection.everyItem;
import org.hamcrest.core.isA;
import org.hamcrest.object.equalTo;

use namespace dolly_internal;

public class PropertyUtilTests {

	private var sourceObj:Object;
	private var targetObj:Object;

	[Before]
	public function before():void {
		sourceObj = {};
		sourceObj.array = [0, 1, 2, 3, 4];
		sourceObj.arrayList = new ArrayList([0, 1, 2, 3, 4]);
		sourceObj.arrayCollection = new ArrayCollection([0, 1, 2, 3, 4]);

		targetObj = {};
	}

	[After]
	public function after():void {
		sourceObj = targetObj = null;
	}

	[Test]
	public function copyingOfArray():void {
		PropertyUtil.copyArray(sourceObj, targetObj, "array");

		assertThat(targetObj.array, arrayWithSize(5));
		assertThat(targetObj.array, sourceObj.array);
		assertTrue(targetObj.array != sourceObj.array);
		assertThat(targetObj.array, everyItem(isA(Number)));
		assertThat(targetObj.array, array(equalTo(0), equalTo(1), equalTo(2), equalTo(3), equalTo(4)));
	}
}
}

package dolly.utils {
import dolly.core.dolly_internal;

import mx.collections.ArrayCollection;
import mx.collections.ArrayList;

import org.flexunit.assertThat;
import org.flexunit.asserts.assertEquals;
import org.flexunit.asserts.assertFalse;
import org.flexunit.asserts.assertNotNull;
import org.flexunit.asserts.assertTrue;
import org.hamcrest.collection.array;
import org.hamcrest.collection.arrayWithSize;
import org.hamcrest.collection.everyItem;
import org.hamcrest.core.isA;
import org.hamcrest.object.equalTo;

use namespace dolly_internal;

public class PropertyUtilTests {

	private var sourceObj:Object;
	private var targetObj:Object;

	[Before]
	public function before():void {
		sourceObj = {};
		sourceObj.array = [0, 1, 2, 3, 4];
		sourceObj.arrayList = new ArrayList([0, 1, 2, 3, 4]);
		sourceObj.arrayCollection = new ArrayCollection([0, 1, 2, 3, 4]);

		targetObj = {};
	}

	[After]
	public function after():void {
		sourceObj = targetObj = null;
	}

	[Test]
	public function copyingOfArray():void {
		PropertyUtil.copyArray(sourceObj, targetObj, "array");

		assertThat(targetObj.array, arrayWithSize(5));
		assertThat(targetObj.array, sourceObj.array);
		assertTrue(targetObj.array != sourceObj.array);
		assertThat(targetObj.array, everyItem(isA(Number)));
		assertThat(targetObj.array, array(equalTo(0), equalTo(1), equalTo(2), equalTo(3), equalTo(4)));
	}

	[Test]
	public function copyingOfArrayList():void {
		PropertyUtil.copyArrayList(sourceObj, targetObj, "arrayList");

		const targetArrayList:ArrayList = targetObj.arrayList;

		assertNotNull(targetArrayList);
		assertFalse(targetObj.arrayList == sourceObj.arrayList);
		assertEquals(targetArrayList.length, 5);
		assertEquals(targetArrayList.getItemAt(0), 0);
		assertEquals(targetArrayList.getItemAt(1), 1);
		assertEquals(targetArrayList.getItemAt(2), 2);
		assertEquals(targetArrayList.getItemAt(3), 3);
		assertEquals(targetArrayList.getItemAt(4), 4);
	}
}
}

Test for PropertyUtil.copyArrayList() method.


package org.hola {
    import flash.events.*;
    import flash.net.URLStream;
    import flash.net.URLRequest;
    import flash.utils.setTimeout;
    import flash.utils.clearTimeout;
    import flash.external.ExternalInterface;
    import org.hola.ZExternalInterface;

    public class FlashFetchBin {
        public static var inited:Boolean = false;
        public static var free_id:Number = 0;
        public static var req_list:Object = {};
        public static function init():Boolean{
            if (inited)
                return inited;
            if (!ZExternalInterface.avail())
                return false;
            ExternalInterface.addCallback('hola_fetchBin', hola_fetchBin);
            ExternalInterface.addCallback('hola_fetchBinRemove',
                hola_fetchBinRemove);
            ExternalInterface.addCallback('hola_fetchBinAbort',
                hola_fetchBinAbort);
            inited = true;
            return inited;
        }
        public static function hola_fetchBin(o:Object):Object{
            var id:String = 'fetch_bin_'+free_id;
            free_id++;
            var url:String = o.url;
            var req:URLRequest = new URLRequest(url);
            var stream:URLStream = new URLStream();
            stream.load(req);
            req_list[id] = {id: id, stream: stream,
                jsurlstream_req_id: o.jsurlstream_req_id};
            stream.addEventListener(Event.OPEN, streamOpen);
            stream.addEventListener(ProgressEvent.PROGRESS, streamProgress);
            stream.addEventListener(HTTPStatusEvent.HTTP_STATUS,
                streamHttpStatus);
            stream.addEventListener(Event.COMPLETE, streamComplete);
            stream.addEventListener(IOErrorEvent.IO_ERROR, streamError);
            stream.addEventListener(SecurityErrorEvent.SECURITY_ERROR,
                streamError);
            return {id: id, url: url};
        }
        public static function hola_fetchBinRemove(id:String):void{
            var req:Object = req_list[id];
            if (!req)
                return;
            if (req.stream.connected)
                req.stream.close();
            if (req.timer)
                clearTimeout(req.timer);
            delete req_list[id];
        }
        public static function hola_fetchBinAbort(id:String):void{
            var req:Object = req_list[id];
            if (!req)
                return;
            if (req.stream.connected)
                req.stream.close();
        }
        public static function getReqFromStream(stream:Object):Object{
            // XXX arik/bahaa: implement without loop
            for (var n:String in req_list)
            {
                if (req_list[n].stream===stream)
                    return req_list[n];
            }
            return null;
        }
        // XXX arik/bahaa: mv to org.hola.util
        public static function jsPostMessage(id:String, data:Object):void{
            if (!ZExternalInterface.avail())
                return;
            ExternalInterface.call('window.postMessage',
                {id: id, ts: new Date().getTime(), data: data}, '*');
        }
        public static function streamOpen(e:Event):void{
            var req:Object = getReqFromStream(e.target);
            if (!req)
                return ZErr.log('req not found streamOpen');
            jsPostMessage('holaflash.streamOpen', {id: req.id});
        }
        public static function streamProgress(e:ProgressEvent):void{
            var req:Object = getReqFromStream(e.target);
            if (!req)
                return ZErr.log('req not found streamProgress');
            req.bytesTotal = e.bytesTotal;
            req.bytesLoaded = e.bytesLoaded;
            if (!req.prevJSProgress || req.bytesLoaded==req.bytesTotal ||
                req.bytesLoaded-req.prevJSProgress > (req.bytesTotal/5))
            {
                req.prevJSProgress = req.bytesLoaded;
                jsPostMessage('holaflash.streamProgress', {id: req.id,
                    bytesLoaded: e.bytesLoaded, bytesTotal: e.bytesTotal});
            }
        }
        public static function streamHttpStatus(e:HTTPStatusEvent):void{
            var req:Object = getReqFromStream(e.target);
            if (!req)
                return ZErr.log('req not found streamHttpStatus');
            jsPostMessage('holaflash.streamHttpStatus', {id: req.id,
                status: e.status});
        }
        public static function streamComplete(e:Event):void{
            var req:Object = getReqFromStream(e.target);
            if (!req)
                return ZErr.log('req not found streamComplete');
            jsPostMessage('holaflash.streamComplete', {id: req.id,
                bytesTotal: req.bytesTotal});
        }
        public static function streamError(e:ErrorEvent):void{
            var req:Object = getReqFromStream(e.target);
            if (!req)
                return ZErr.log('req not found streamError');
            jsPostMessage('holaflash.streamError', {id: req.id});
            hola_fetchBinRemove(req.id);
        }
        public static function consumeDataTimeout(id:String,
            cb:Function, ms:Number, ctx:Object):void{
            var req:Object = req_list[id];
            if (!req)
                throw new Error('consumeDataTimeout failed find req '+id);
            if (req.timer)
                clearTimeout(req.timer);
            req.timer = setTimeout(cb, ms, ctx);
        }
    }
}

package org.hola {
    import flash.events.*;
    import flash.net.URLStream;
    import flash.net.URLRequest;
    import flash.utils.setTimeout;
    import flash.utils.clearTimeout;
    import flash.external.ExternalInterface;
    import org.hola.ZExternalInterface;

    public class FlashFetchBin {
        private static var inited:Boolean = false;
        private static var free_id:Number = 0;
        public static var req_list:Object = {};
        public static function init():Boolean{
            if (inited)
                return inited;
            if (!ZExternalInterface.avail())
                return false;
            ExternalInterface.addCallback('hola_fetchBin', hola_fetchBin);
            ExternalInterface.addCallback('hola_fetchBinRemove',
                hola_fetchBinRemove);
            ExternalInterface.addCallback('hola_fetchBinAbort',
                hola_fetchBinAbort);
            inited = true;
            return inited;
        }
        private static function hola_fetchBin(o:Object):Object{
            var id:String = 'fetch_bin_'+free_id;
            free_id++;
            var url:String = o.url;
            var req:URLRequest = new URLRequest(url);
            var stream:URLStream = new URLStream();
            stream.load(req);
            req_list[id] = {id: id, stream: stream,
                jsurlstream_req_id: o.jsurlstream_req_id};
            stream.addEventListener(Event.OPEN, streamOpen);
            stream.addEventListener(ProgressEvent.PROGRESS, streamProgress);
            stream.addEventListener(HTTPStatusEvent.HTTP_STATUS,
                streamHttpStatus);
            stream.addEventListener(Event.COMPLETE, streamComplete);
            stream.addEventListener(IOErrorEvent.IO_ERROR, streamError);
            stream.addEventListener(SecurityErrorEvent.SECURITY_ERROR,
                streamError);
            return {id: id, url: url};
        }
        public static function hola_fetchBinRemove(id:String):void{
            var req:Object = req_list[id];
            if (!req)
                return;
            if (req.stream.connected)
                req.stream.close();
            if (req.timer)
                clearTimeout(req.timer);
            delete req_list[id];
        }
        private static function hola_fetchBinAbort(id:String):void{
            var req:Object = req_list[id];
            if (!req)
                return;
            if (req.stream.connected)
                req.stream.close();
        }
        private static function getReqFromStream(stream:Object):Object{
            // XXX arik/bahaa: implement without loop
            for (var n:String in req_list)
            {
                if (req_list[n].stream===stream)
                    return req_list[n];
            }
            return null;
        }
        // XXX arik/bahaa: mv to org.hola.util
        private static function jsPostMessage(id:String, data:Object):void{
            if (!ZExternalInterface.avail())
                return;
            ExternalInterface.call('window.postMessage',
                {id: id, ts: new Date().getTime(), data: data}, '*');
        }
        private static function streamOpen(e:Event):void{
            var req:Object = getReqFromStream(e.target);
            if (!req)
                return ZErr.log('req not found streamOpen');
            jsPostMessage('holaflash.streamOpen', {id: req.id});
        }
        private static function streamProgress(e:ProgressEvent):void{
            var req:Object = getReqFromStream(e.target);
            if (!req)
                return ZErr.log('req not found streamProgress');
            req.bytesTotal = e.bytesTotal;
            req.bytesLoaded = e.bytesLoaded;
            if (!req.prevJSProgress || req.bytesLoaded==req.bytesTotal ||
                req.bytesLoaded-req.prevJSProgress > (req.bytesTotal/5))
            {
                req.prevJSProgress = req.bytesLoaded;
                jsPostMessage('holaflash.streamProgress', {id: req.id,
                    bytesLoaded: e.bytesLoaded, bytesTotal: e.bytesTotal});
            }
        }
        private static function streamHttpStatus(e:HTTPStatusEvent):void{
            var req:Object = getReqFromStream(e.target);
            if (!req)
                return ZErr.log('req not found streamHttpStatus');
            jsPostMessage('holaflash.streamHttpStatus', {id: req.id,
                status: e.status});
        }
        private static function streamComplete(e:Event):void{
            var req:Object = getReqFromStream(e.target);
            if (!req)
                return ZErr.log('req not found streamComplete');
            jsPostMessage('holaflash.streamComplete', {id: req.id,
                bytesTotal: req.bytesTotal});
        }
        private static function streamError(e:ErrorEvent):void{
            var req:Object = getReqFromStream(e.target);
            if (!req)
                return ZErr.log('req not found streamError');
            jsPostMessage('holaflash.streamError', {id: req.id});
            hola_fetchBinRemove(req.id);
        }
        public static function consumeDataTimeout(id:String,
            cb:Function, ms:Number, ctx:Object):void{
            var req:Object = req_list[id];
            if (!req)
                throw new Error('consumeDataTimeout failed find req '+id);
            if (req.timer)
                clearTimeout(req.timer);
            req.timer = setTimeout(cb, ms, ctx);
        }
    }
}

make private members private

make private members private


package org.jbei.components.sequenceClasses
{
	import flash.display.Graphics;
	import flash.geom.Rectangle;

	import org.jbei.bio.sequence.common.StrandType;
	import org.jbei.bio.sequence.dna.Feature;
	import org.jbei.components.common.AnnotationRenderer;
	import org.jbei.components.common.IContentHolder;

    /**
     * @author Zinovii Dmytriv
     */
	public class FeatureRenderer extends AnnotationRenderer
	{
		public static const DEFAULT_FEATURE_HEIGHT:int = 6;
		public static const DEFAULT_FEATURES_SEQUENCE_GAP:int = 3;
		public static const DEFAULT_FEATURES_GAP:int = 2;

		private var sequenceContentHolder:ContentHolder;

		// Contructor
		public function FeatureRenderer(contentHolder:IContentHolder, feature:Feature)
		{
			super(contentHolder, feature);

			sequenceContentHolder = contentHolder as ContentHolder;
		}

		// Properties
		public function get feature():Feature
		{
			return annotation as Feature;
		}

		// Public Methods
		public function update():void
		{
			needsMeasurement = true;
			invalidateDisplayList();
		}

		// Protected Methods
		protected override function render():void
		{
			super.render();

			var g:Graphics = graphics;
			g.clear();

			var featureRows:Array = sequenceContentHolder.rowMapper.featureToRowMap[feature];

			if(! featureRows) { return; }

			for(var i:int = 0; i < featureRows.length; i++) {
				var row:Row = sequenceContentHolder.rowMapper.rows[featureRows[i]] as Row;

				// find feature row index
				var alignmentRowIndex:int = -1;

				for(var r:int = 0; r < row.rowData.featuresAlignment.length; r++) {
					var rowFeatures:Array = row.rowData.featuresAlignment[r] as Array;

					for(var c:int = 0; c < rowFeatures.length; c++) {
						if((rowFeatures[c] as Feature) == feature) {
							alignmentRowIndex = r;
							break;
						}
					}

					if(alignmentRowIndex != -1) { break; }
				}

				g.lineStyle(1, 0x606060);
				g.beginFill(colorByType(feature.type.toLowerCase()));

				var startBP:int;
				var endBP:int;

				if(feature.start > feature.end) { // circular case
					/* |--------------------------------------------------------------------------------------|
					*  FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF|                                             */
					if(feature.end >= row.rowData.start && feature.end <= row.rowData.end) {
						endBP = feature.end - 1;
					}
						/* |--------------------------------------------------------------------------------------|
						*  FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF  */
					else if(row.rowData.end >= contentHolder.sequenceProvider.sequence.length) {
						endBP = contentHolder.sequenceProvider.sequence.length - 1;
					}
					else {
						endBP = row.rowData.end;
					}

					/* |--------------------------------------------------------------------------------------|
					*                                    |FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF  */
					if(feature.start >= row.rowData.start && feature.start <= row.rowData.end) {
						startBP = feature.start;
					}
						/* |--------------------------------------------------------------------------------------|
						*   FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF  */
					else {
						startBP = row.rowData.start;
					}
				} else {
					startBP = (feature.start < row.rowData.start) ? row.rowData.start : feature.start;
					endBP = (feature.end - 1 < row.rowData.end) ? feature.end - 1 : row.rowData.end;
				}

				/* Case when start and end are in the same row
				* |--------------------------------------------------------------------------------------|
				*  FFFFFFFFFFFFFFFFFFFFFFFFFFF|                     |FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF  */
				if(startBP > endBP) {
					var bpStartMetrics1:Rectangle = sequenceContentHolder.bpMetricsByIndex(row.rowData.start);
					var bpEndMetrics1:Rectangle = sequenceContentHolder.bpMetricsByIndex(Math.min(endBP, contentHolder.sequenceProvider.sequence.length - 1));

					var bpStartMetrics2:Rectangle = sequenceContentHolder.bpMetricsByIndex(startBP);
					var bpEndMetrics2:Rectangle = sequenceContentHolder.bpMetricsByIndex(Math.min(row.rowData.end, contentHolder.sequenceProvider.sequence.length - 1));

					var featureX1:Number = bpStartMetrics1.x + 2; // +2 to look pretty
					var featureX2:Number = bpStartMetrics2.x + 2; // +2 to look pretty
					var featureYCommon:Number = bpStartMetrics1.y + row.sequenceMetrics.height + alignmentRowIndex * (DEFAULT_FEATURE_HEIGHT + DEFAULT_FEATURES_GAP) + DEFAULT_FEATURES_SEQUENCE_GAP;

					if(sequenceContentHolder.showAminoAcids1RevCom) {
						featureYCommon += 3 * sequenceContentHolder.aminoAcidsTextRenderer.textHeight;
					}

					var featureRowWidth1:Number = bpEndMetrics1.x - bpStartMetrics1.x + sequenceContentHolder.sequenceSymbolRenderer.textWidth;
					var featureRowWidth2:Number = bpEndMetrics2.x - bpStartMetrics2.x + sequenceContentHolder.sequenceSymbolRenderer.textWidth;

					var featureRowHeightCommon:Number = DEFAULT_FEATURE_HEIGHT;

					if(feature.strand == StrandType.UNKNOWN) {
						drawFeatureRect(g, featureX1, featureYCommon, featureRowWidth1, featureRowHeightCommon);
						drawFeatureRect(g, featureX2, featureYCommon, featureRowWidth2, featureRowHeightCommon);
					} else if(feature.strand == StrandType.FORWARD) {
						drawFeatureForwardArrow(g, featureX1, featureYCommon, featureRowWidth1, featureRowHeightCommon);
						drawFeatureForwardRect(g, featureX2, featureYCommon, featureRowWidth2, featureRowHeightCommon);
					} else if(feature.strand == StrandType.BACKWARD) {
						drawFeatureBackwardRect(g, featureX1, featureYCommon, featureRowWidth1, featureRowHeightCommon);
						drawFeatureBackwardArrow(g, featureX2, featureYCommon, featureRowWidth2, featureRowHeightCommon);
					}
				} else {
					var bpStartMetrics:Rectangle = sequenceContentHolder.bpMetricsByIndex(startBP);
					var bpEndMetrics:Rectangle = sequenceContentHolder.bpMetricsByIndex(Math.min(endBP, contentHolder.sequenceProvider.sequence.length - 1));

					var featureX:Number = bpStartMetrics.x + 2; // +2 to look pretty
					var featureY:Number = bpStartMetrics.y + row.sequenceMetrics.height + alignmentRowIndex * (DEFAULT_FEATURE_HEIGHT + DEFAULT_FEATURES_GAP) + DEFAULT_FEATURES_SEQUENCE_GAP;

					if(sequenceContentHolder.showAminoAcids1RevCom) {
						featureY += 3 * sequenceContentHolder.aminoAcidsTextRenderer.textHeight;
					}

					var featureRowWidth:Number = bpEndMetrics.x - bpStartMetrics.x + sequenceContentHolder.sequenceSymbolRenderer.textWidth;
					var featureRowHeight:Number = DEFAULT_FEATURE_HEIGHT;

					if(feature.strand == StrandType.UNKNOWN) {
						drawFeatureRect(g, featureX, featureY, featureRowWidth, featureRowHeight);
					} else if(feature.strand == StrandType.FORWARD) {
						if(feature.end >= row.rowData.start && feature.end <= row.rowData.end) {
							drawFeatureForwardArrow(g, featureX, featureY, featureRowWidth, featureRowHeight);
						} else {
							drawFeatureForwardRect(g, featureX, featureY, featureRowWidth, featureRowHeight);
						}
					} else if(feature.strand == StrandType.BACKWARD) {
						if(feature.start >= row.rowData.start && feature.start <= row.rowData.end) {
							drawFeatureBackwardArrow(g, featureX, featureY, featureRowWidth, featureRowHeight);
						} else {
							drawFeatureBackwardRect(g, featureX, featureY, featureRowWidth, featureRowHeight);
						}
					}
				}

				g.endFill();
			}
		}

		protected override function createToolTipLabel():void
		{
			tooltipLabel = feature.type + (feature.name == "" ? "" : (" - " + feature.name)) + ": " + (feature.start + 1) + ".." + (feature.end);
		}

		// Private Methods
		private function colorByType(featureType:String):int
		{
			var color:int = 0xCCCCCC;

			if(featureType == "promoter") {
				color = 0x31B440;
			} else if(featureType == "terminator"){
				color = 0xF51600;
			} else if(featureType == "cds"){
				color = 0xEF6500;
			} else if(featureType == "m_rna"){
				color = 0xFFFF00;
			} else if(featureType == "misc_binding"){
				color = 0x006FEF;
			} else if(featureType == "misc_feature"){
				color = 0x006FEF;
			} else if(featureType == "misc_marker"){
				color = 0x8DCEB1;
			} else if(featureType == "rep_origin"){
				color = 0x878787;
			}

			return color;
		}

		private function drawFeatureRect(g:Graphics, x:Number, y:Number, width:Number, height:Number):void
		{
			g.drawRect(x, y, width, height);
		}

		private function drawFeatureForwardRect(g:Graphics, x:Number, y:Number, width:Number, height:Number):void
		{
			g.moveTo(x, y);
			g.curveTo(x + 3, y + height / 2, x, y + height);
			g.lineTo(x + width, y + height);
			g.lineTo(x + width, y);
			g.lineTo(x, y);
		}

		private function drawFeatureBackwardRect(g:Graphics, x:Number, y:Number, width:Number, height:Number):void
		{
			g.moveTo(x, y);
			g.lineTo(x, y + height);
			g.lineTo(x + width, y + height);
			g.curveTo(x + width - 3, y + height / 2, x + width, y);
			g.lineTo(x, y);
		}

		private function drawFeatureForwardArrow(g:Graphics, x:Number, y:Number, width:Number, height:Number):void
		{
			if(width > sequenceContentHolder.sequenceSymbolRenderer.width) {
				g.moveTo(x, y);
				g.lineTo(x + width - 8, y);
				g.lineTo(x + width, y + height / 2);
				g.lineTo(x + width - 8, y + height);
				g.lineTo(x, y + height);
				g.curveTo(x + 3, y + height / 2, x, y);
			} else {
				g.moveTo(x, y);
				g.lineTo(x + width, y + height / 2);
				g.lineTo(x, y + height);
				g.lineTo(x, y);
			}
		}

		private function drawFeatureBackwardArrow(g:Graphics, x:Number, y:Number, width:Number, height:Number):void
		{
			if(width > sequenceContentHolder.sequenceSymbolRenderer.width) {
				g.moveTo(x + 8, y);
				g.lineTo(x + width, y);
				g.curveTo(x + width - 3, y + height / 2, x + width, y + height);
				g.lineTo(x + 8, y + height);
				g.lineTo(x, y + height / 2);
				g.lineTo(x + 8, y);
			} else {
				g.moveTo(x, y + height / 2);
				g.lineTo(x + width, y);
				g.lineTo(x + width, y + height);
				g.lineTo(x, y + height / 2);
			}
		}
	}
}

package org.jbei.components.sequenceClasses
{
	import flash.display.Graphics;
	import flash.geom.Rectangle;

	import org.jbei.bio.sequence.common.StrandType;
	import org.jbei.bio.sequence.dna.Feature;
	import org.jbei.components.common.AnnotationRenderer;
	import org.jbei.components.common.IContentHolder;

    /**
     * @author Zinovii Dmytriv
     */
	public class FeatureRenderer extends AnnotationRenderer
	{
		public static const DEFAULT_FEATURE_HEIGHT:int = 6;
		public static const DEFAULT_FEATURES_SEQUENCE_GAP:int = 3;
		public static const DEFAULT_FEATURES_GAP:int = 2;

		private var sequenceContentHolder:ContentHolder;

		// Contructor
		public function FeatureRenderer(contentHolder:IContentHolder, feature:Feature)
		{
			super(contentHolder, feature);

			sequenceContentHolder = contentHolder as ContentHolder;
		}

		// Properties
		public function get feature():Feature
		{
			return annotation as Feature;
		}

		// Public Methods
		public function update():void
		{
			needsMeasurement = true;
			invalidateDisplayList();
		}

		// Protected Methods
		protected override function render():void
		{
			super.render();

			var g:Graphics = graphics;
			g.clear();

			var featureRows:Array = sequenceContentHolder.rowMapper.featureToRowMap[feature];

			if(! featureRows) { return; }

			for(var i:int = 0; i < featureRows.length; i++) {
				var row:Row = sequenceContentHolder.rowMapper.rows[featureRows[i]] as Row;

				// find feature row index
				var alignmentRowIndex:int = -1;

				for(var r:int = 0; r < row.rowData.featuresAlignment.length; r++) {
					var rowFeatures:Array = row.rowData.featuresAlignment[r] as Array;

					for(var c:int = 0; c < rowFeatures.length; c++) {
						if((rowFeatures[c] as Feature) == feature) {
							alignmentRowIndex = r;
							break;
						}
					}

					if(alignmentRowIndex != -1) { break; }
				}

				g.lineStyle(1, 0x606060);
				g.beginFill(colorByType(feature.type.toLowerCase()));

				var startBP:int;
				var endBP:int;

				if(feature.start > feature.end) { // circular case
					/* |--------------------------------------------------------------------------------------|
					*  FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF|                                             */
					if(feature.end >= row.rowData.start && feature.end <= row.rowData.end) {
						endBP = feature.end - 1;
					}
						/* |--------------------------------------------------------------------------------------|
						*  FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF  */
					else if(row.rowData.end >= contentHolder.sequenceProvider.sequence.length) {
						endBP = contentHolder.sequenceProvider.sequence.length - 1;
					}
					else {
						endBP = row.rowData.end;
					}

					/* |--------------------------------------------------------------------------------------|
					*                                    |FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF  */
					if(feature.start >= row.rowData.start && feature.start <= row.rowData.end) {
						startBP = feature.start;
					}
						/* |--------------------------------------------------------------------------------------|
						*   FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF  */
					else {
						startBP = row.rowData.start;
					}
				} else {
                    if (feature.start < row.rowData.start && feature.end <= row.rowData.start) {
                        continue; // the feature is outside of the current row
                    } else {
    					startBP = (feature.start < row.rowData.start) ? row.rowData.start : feature.start;
    					endBP = (feature.end - 1 < row.rowData.end) ? feature.end - 1 : row.rowData.end;
                    }
				}

				/* Case when start and end are in the same row
				* |--------------------------------------------------------------------------------------|
				*  FFFFFFFFFFFFFFFFFFFFFFFFFFF|                     |FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF  */
				if(startBP > endBP) {
					var bpStartMetrics1:Rectangle = sequenceContentHolder.bpMetricsByIndex(row.rowData.start);
					var bpEndMetrics1:Rectangle = sequenceContentHolder.bpMetricsByIndex(Math.min(endBP, contentHolder.sequenceProvider.sequence.length - 1));

					var bpStartMetrics2:Rectangle = sequenceContentHolder.bpMetricsByIndex(startBP);
					var bpEndMetrics2:Rectangle = sequenceContentHolder.bpMetricsByIndex(Math.min(row.rowData.end, contentHolder.sequenceProvider.sequence.length - 1));

					var featureX1:Number = bpStartMetrics1.x + 2; // +2 to look pretty
					var featureX2:Number = bpStartMetrics2.x + 2; // +2 to look pretty
					var featureYCommon:Number = bpStartMetrics1.y + row.sequenceMetrics.height + alignmentRowIndex * (DEFAULT_FEATURE_HEIGHT + DEFAULT_FEATURES_GAP) + DEFAULT_FEATURES_SEQUENCE_GAP;

					if(sequenceContentHolder.showAminoAcids1RevCom) {
						featureYCommon += 3 * sequenceContentHolder.aminoAcidsTextRenderer.textHeight;
					}

					var featureRowWidth1:Number = bpEndMetrics1.x - bpStartMetrics1.x + sequenceContentHolder.sequenceSymbolRenderer.textWidth;
					var featureRowWidth2:Number = bpEndMetrics2.x - bpStartMetrics2.x + sequenceContentHolder.sequenceSymbolRenderer.textWidth;

					var featureRowHeightCommon:Number = DEFAULT_FEATURE_HEIGHT;

					if(feature.strand == StrandType.UNKNOWN) {
						drawFeatureRect(g, featureX1, featureYCommon, featureRowWidth1, featureRowHeightCommon);
						drawFeatureRect(g, featureX2, featureYCommon, featureRowWidth2, featureRowHeightCommon);
					} else if(feature.strand == StrandType.FORWARD) {
						drawFeatureForwardArrow(g, featureX1, featureYCommon, featureRowWidth1, featureRowHeightCommon);
						drawFeatureForwardRect(g, featureX2, featureYCommon, featureRowWidth2, featureRowHeightCommon);
					} else if(feature.strand == StrandType.BACKWARD) {
						drawFeatureBackwardRect(g, featureX1, featureYCommon, featureRowWidth1, featureRowHeightCommon);
						drawFeatureBackwardArrow(g, featureX2, featureYCommon, featureRowWidth2, featureRowHeightCommon);
					}
				} else {
					var bpStartMetrics:Rectangle = sequenceContentHolder.bpMetricsByIndex(startBP);
					var bpEndMetrics:Rectangle = sequenceContentHolder.bpMetricsByIndex(Math.min(endBP, contentHolder.sequenceProvider.sequence.length - 1));

					var featureX:Number = bpStartMetrics.x + 2; // +2 to look pretty
					var featureY:Number = bpStartMetrics.y + row.sequenceMetrics.height + alignmentRowIndex * (DEFAULT_FEATURE_HEIGHT + DEFAULT_FEATURES_GAP) + DEFAULT_FEATURES_SEQUENCE_GAP;

					if(sequenceContentHolder.showAminoAcids1RevCom) {
						featureY += 3 * sequenceContentHolder.aminoAcidsTextRenderer.textHeight;
					}

					var featureRowWidth:Number = bpEndMetrics.x - bpStartMetrics.x + sequenceContentHolder.sequenceSymbolRenderer.textWidth;
					var featureRowHeight:Number = DEFAULT_FEATURE_HEIGHT;

					if(feature.strand == StrandType.UNKNOWN) {
						drawFeatureRect(g, featureX, featureY, featureRowWidth, featureRowHeight);
					} else if(feature.strand == StrandType.FORWARD) {
						if(feature.end >= row.rowData.start && feature.end <= row.rowData.end + 1) {
							drawFeatureForwardArrow(g, featureX, featureY, featureRowWidth, featureRowHeight);
						} else {
							drawFeatureForwardRect(g, featureX, featureY, featureRowWidth, featureRowHeight);
						}
					} else if(feature.strand == StrandType.BACKWARD) {
						if(feature.start >= row.rowData.start && feature.start <= row.rowData.end) {
							drawFeatureBackwardArrow(g, featureX, featureY, featureRowWidth, featureRowHeight);
						} else {
							drawFeatureBackwardRect(g, featureX, featureY, featureRowWidth, featureRowHeight);
						}
					}
				}

				g.endFill();
			}
		}

		protected override function createToolTipLabel():void
		{
			tooltipLabel = feature.type + (feature.name == "" ? "" : (" - " + feature.name)) + ": " + (feature.start + 1) + ".." + (feature.end);
		}

		// Private Methods
		private function colorByType(featureType:String):int
		{
			var color:int = 0xCCCCCC;

			if(featureType == "promoter") {
				color = 0x31B440;
			} else if(featureType == "terminator"){
				color = 0xF51600;
			} else if(featureType == "cds"){
				color = 0xEF6500;
			} else if(featureType == "m_rna"){
				color = 0xFFFF00;
			} else if(featureType == "misc_binding"){
				color = 0x006FEF;
			} else if(featureType == "misc_feature"){
				color = 0x006FEF;
			} else if(featureType == "misc_marker"){
				color = 0x8DCEB1;
			} else if(featureType == "rep_origin"){
				color = 0x878787;
			}

			return color;
		}

		private function drawFeatureRect(g:Graphics, x:Number, y:Number, width:Number, height:Number):void
		{
			g.drawRect(x, y, width, height);
		}

		private function drawFeatureForwardRect(g:Graphics, x:Number, y:Number, width:Number, height:Number):void
		{
			g.moveTo(x, y);
			g.curveTo(x + 3, y + height / 2, x, y + height);
			g.lineTo(x + width, y + height);
			g.lineTo(x + width, y);
			g.lineTo(x, y);
		}

		private function drawFeatureBackwardRect(g:Graphics, x:Number, y:Number, width:Number, height:Number):void
		{
			g.moveTo(x, y);
			g.lineTo(x, y + height);
			g.lineTo(x + width, y + height);
			g.curveTo(x + width - 3, y + height / 2, x + width, y);
			g.lineTo(x, y);
		}

		private function drawFeatureForwardArrow(g:Graphics, x:Number, y:Number, width:Number, height:Number):void
		{
			if(width > sequenceContentHolder.sequenceSymbolRenderer.width) {
				g.moveTo(x, y);
				g.lineTo(x + width - 8, y);
				g.lineTo(x + width, y + height / 2);
				g.lineTo(x + width - 8, y + height);
				g.lineTo(x, y + height);
				g.curveTo(x + 3, y + height / 2, x, y);
			} else {
				g.moveTo(x, y);
				g.lineTo(x + width, y + height / 2);
				g.lineTo(x, y + height);
				g.lineTo(x, y);
			}
		}

		private function drawFeatureBackwardArrow(g:Graphics, x:Number, y:Number, width:Number, height:Number):void
		{
			if(width > sequenceContentHolder.sequenceSymbolRenderer.width) {
				g.moveTo(x + 8, y);
				g.lineTo(x + width, y);
				g.curveTo(x + width - 3, y + height / 2, x + width, y + height);
				g.lineTo(x + 8, y + height);
				g.lineTo(x, y + height / 2);
				g.lineTo(x + 8, y);
			} else {
				g.moveTo(x, y + height / 2);
				g.lineTo(x + width, y);
				g.lineTo(x + width, y + height);
				g.lineTo(x, y + height / 2);
			}
		}
	}
}

fix feature rendering bug

[VectorCommon] fix feature rendering bug

git-svn-id: adbdea8fc1759bd16b0eaf24a244a7e5f870a654@280 fe3f0490-d73e-11de-a956-c71e7d3a9d2f


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

    /**
     *  The SimpleCSSStyles class contains CSS style
     *  properties supported by SimpleCSSValuesImpl.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10.2
     *  @playerversion AIR 2.6
     *  @productversion FlexJS 0.0
     */
	public class SimpleCSSStyles
	{
        /**
         *  Constructor.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function SimpleCSSStyles()
		{
			super();
		}

		private var _object:IStyleableObject;

        /**
         *  The object to which these styles apply.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function get object():IStyleableObject
		{
			return _object;
		}

        /**
         *  @private
         */
		public function set object(value:IStyleableObject):void
		{
			if (_object != value)
			{
                _object = value;
			}
		}

        public var top:*;
        public var bottom:*;
        public var left:*;
        public var right:*;
        public var padding:*;
		public var paddingLeft:*;
        public var paddingRight:*;
        public var paddingTop:*;
        public var paddingBottom:*;
        public var horizontalCenter:*;
        public var verticalCenter:*;
        public var horizontalAlign:*;
        public var verticalAlign:*;
        public var fontFamily:*;
        public var fontSize:*;
        public var color:*;
        public var backgroundAlpha:*;
        public var backgroundColor:*;
        public var backgroundImage:*;
        public var borderColor:*;
        public var borderStyle:*;
        public var borderRadius:*;
	}
}

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

    /**
     *  The SimpleCSSStyles class contains CSS style
     *  properties supported by SimpleCSSValuesImpl.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10.2
     *  @playerversion AIR 2.6
     *  @productversion FlexJS 0.0
     */
	public class SimpleCSSStyles
	{
        /**
         *  Constructor.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function SimpleCSSStyles()
		{
			super();
		}

		private var _object:IStyleableObject;

        /**
         *  The object to which these styles apply.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function get object():IStyleableObject
		{
			return _object;
		}

        /**
         *  @private
         */
		public function set object(value:IStyleableObject):void
		{
			if (_object != value)
			{
                _object = value;
			}
		}

        public var top:*;
        public var bottom:*;
        public var left:*;
        public var right:*;
        public var padding:*;
		public var paddingLeft:*;
        public var paddingRight:*;
        public var paddingTop:*;
        public var paddingBottom:*;
        public var horizontalCenter:*;
        public var verticalCenter:*;
        public var horizontalAlign:*;
        public var verticalAlign:*;
        public var fontFamily:*;
        public var fontSize:*;
        public var color:*;
        public var fontWeight:*;
        public var fontStyle:*;
        public var backgroundAlpha:*;
        public var backgroundColor:*;
        public var backgroundImage:*;
        public var borderColor:*;
        public var borderStyle:*;
        public var borderRadius:*;
	}
}

add a few more styles

add a few more styles


/*
* Copyright (C) 2003-2011 Igniterealtime Community Contributors
*
*     Daniel Henninger
*     Derrick Grigg <dgrigg@rogers.com>
*     Juga Paazmaya <olavic@gmail.com>
*     Nick Velloff <nick.velloff@gmail.com>
*     Sean Treadway <seant@oncotype.dk>
*     Sean Voisen <sean@voisen.org>
*     Mark Walters <mark@yourpalmark.com>
*     Michael McCarthy <mikeycmccarthy@gmail.com>
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.igniterealtime.xiff.util
{
	import org.flexunit.asserts.assertEquals;

	public class DateTimeParserTest
	{
		[Test]
		public function testDate2String():void
		{
			var date:Date = new Date( 2000, 10, 30, 0, 0 );
			var dateAsString:String = DateTimeParser.date2string( date );
			assertEquals( "2000-12-30", dateAsString );
		}
	}
}

/*
* Copyright (C) 2003-2011 Igniterealtime Community Contributors
*
*     Daniel Henninger
*     Derrick Grigg <dgrigg@rogers.com>
*     Juga Paazmaya <olavic@gmail.com>
*     Nick Velloff <nick.velloff@gmail.com>
*     Sean Treadway <seant@oncotype.dk>
*     Sean Voisen <sean@voisen.org>
*     Mark Walters <mark@yourpalmark.com>
*     Michael McCarthy <mikeycmccarthy@gmail.com>
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.igniterealtime.xiff.util
{
	import org.flexunit.asserts.assertEquals;

	public class DateTimeParserTest
	{
		[Test]
		public function testDate2String():void
		{
			var date:Date = new Date( 2000, 10, 30, 0, 0 );
			var dateAsString:String = DateTimeParser.date2string( date );
			assertEquals( "2000-11-30", dateAsString );
		}
	}
}

Revert of commit test.

Revert of commit test.

git-svn-id: c197267f952b24206666de142881703007ca05d5@12597 b35dd754-fafc-0310-a699-88a17e54d16e


package aerys.minko.scene.node.mesh
{
	import aerys.minko.ns.minko;
	import aerys.minko.ns.minko_stream;
	import aerys.minko.scene.action.mesh.MeshAction;
	import aerys.minko.scene.node.AbstractScene;
	import aerys.minko.type.stream.IVertexStream;
	import aerys.minko.type.stream.IndexStream;
	import aerys.minko.type.stream.VertexStream;
	import aerys.minko.type.stream.VertexStreamList;
	import aerys.minko.type.vertex.format.VertexComponent;
	import aerys.minko.type.vertex.format.VertexFormat;

	public class Mesh extends AbstractScene implements IMesh
	{
		use namespace minko;
		use namespace minko_stream;

		private static var _id : uint = 0;

		protected var _vertexStream	: IVertexStream	= null;
		protected var _indexStream	: IndexStream	= null;

		public function get version() : uint
		{
			return _vertexStream.version + _indexStream.version;
		}

		public function get vertexStream() : IVertexStream
		{
			return _vertexStream;
		}

		public function set vertexStream(value : IVertexStream) : void
		{
			_vertexStream = value;
		}

		public function get indexStream() : IndexStream
		{
			return _indexStream;
		}

		public function set indexStream(value : IndexStream) : void
		{
			_indexStream = value;
		}

		public function Mesh(vertexStream	: IVertexStream	= null,
							 indexStream	: IndexStream 	= null)
		{
			super();

			_vertexStream = vertexStream;
			_indexStream = indexStream;

			initialize();
		}

		private function initialize() : void
		{
			if (!_indexStream && _vertexStream)
				_indexStream = new IndexStream(null, vertexStream.length, vertexStream.dynamic);

			actions[0] = MeshAction.meshAction;
		}

		/**
		 * Create a new copy of the Mesh object with the specified VertexFormat.
		 *
		 *

		 * If no VertexFormat is specified, the original VertexFormat of the Mesh
		 * will be used. Calling the "clone" method and specifying a VertexFormat
		 * is a good way to create a lighter Mesh by copying only the required
		 * vertex components data.
		 *

		 *
		 * @param vertexFormat
		 * @return
		 *
		 */
		public function clone(vertexFormat : VertexFormat = null) : IMesh
		{
			vertexFormat ||= _vertexStream.format.clone();

			var meshes				: Vector.<IMesh>			= Vector.<IMesh>([this])
			var vertexComponents	: Vector.<VertexComponent>	= vertexFormat.componentList;
			var mergedMesh			: IMesh						= createMesh(meshes, vertexComponents, vertexFormat);

			return mergedMesh;
		}

		/**
		 * The "mergeMeshes" method will takes multiple Mesh objects and will
		 * merge all their data to create a single and only Mesh out of it.
		 *
		 *

		 * Because all the Mesh objects can have multiple VertexFormat, the
		 * result Mesh VertexFormat will be the biggest common set of
		 * VertexComponent of all the merged meshes.
		 *

		 *
		 * @param meshes
		 * @return
		 *
		 */
		public static function mergeMeshes(meshes : Vector.<IMesh>) : IMesh
		{
			var vertexFormat		: VertexFormat				= computeVertexFormat(meshes);
			var vertexComponents	: Vector.<VertexComponent>	= vertexFormat.componentList;
			var mergedMesh			: IMesh						= createMesh(meshes, vertexComponents, vertexFormat);

			return mergedMesh;
		}

		private static function computeVertexFormat(meshes : Vector.<IMesh>) : VertexFormat
		{
			var newVertexFormat : VertexFormat = meshes[0].vertexStream.format.clone();

			for each (var mesh : IMesh in meshes)
				newVertexFormat.intersectWith(mesh.vertexStream.format);

			return newVertexFormat;
		}

		private static function createMesh(meshes			: Vector.<IMesh>,
										   vertexComponents	: Vector.<VertexComponent>,
										   vertexFormat		: VertexFormat) : IMesh
		{
			var newIndexStreamData 				: Vector.<uint>				= new Vector.<uint>();
			var newVertexStreamData				: Vector.<Number>			= new Vector.<Number>();

			var componentCount					: uint						= vertexComponents.length;
			var vertexComponentOffsets			: Vector.<uint>				= new Vector.<uint>(componentCount, true);
			var vertexComponentSizes			: Vector.<uint>				= new Vector.<uint>(componentCount, true);
			var vertexComponentDwordsPerVertex	: Vector.<uint>				= new Vector.<uint>(componentCount, true);
			var vertexComponentDatas			: Vector.<Vector.<Number>>	= new Vector.<Vector.<Number>>(componentCount, true);

			var meshCount : uint = meshes.length;
			for (var i : uint = 0; i < meshCount; ++i)
			{
				var mesh		: IMesh				= meshes[i];

				// append index data
				var indexData	: Vector.<uint>		= mesh.indexStream.getIndices();
				var indexCount	: uint				= indexData.length;
				var indexOffset	: uint				= newVertexStreamData.length / vertexFormat.dwordsPerVertex;

				for (var j : uint = 0; j < indexCount; ++j)
					newIndexStreamData.push(indexData[j] + indexOffset);

				// get offsets, sizes, and buffers for components in the current mesh.
				for (var k : uint = 0; k < componentCount; ++k)
				{
					var vertexComponent	: VertexComponent	= vertexComponents[k];
					var vertexStream	: VertexStream		= mesh.vertexStream.getSubStreamByComponent(vertexComponent);

					vertexComponentOffsets[k]				= vertexStream.format.getOffsetForComponent(vertexComponent);
					vertexComponentDwordsPerVertex[k]		= vertexStream.format.dwordsPerVertex;
					vertexComponentSizes[k]					= vertexComponent.dwords;
					vertexComponentDatas[k]					= vertexStream._data;
				}

				// push vertex data into the new buffer.
				var vertexCount : uint = mesh.vertexStream.length;
				for (var vertexId : uint = 0; vertexId < vertexCount; ++vertexId)
					for (var componentId : uint = 0; componentId < componentCount; ++componentId)
					{
						var vertexData		: Vector.<Number>	= vertexComponentDatas[componentId];
						var componentSize	: uint				= vertexComponentSizes[componentId];
						var componentOffset	: uint				= vertexComponentOffsets[componentId] + vertexId * vertexComponentDwordsPerVertex[componentId];
						var componentLimit	: uint				= componentSize + componentOffset;

						for (var n : uint = componentOffset; n < componentLimit; ++n)
							newVertexStreamData.push(vertexData[n]);
					}
			}

			var newIndexStream		: IndexStream		= new IndexStream(newIndexStreamData);
			var newVertexStream		: VertexStream		= new VertexStream(newVertexStreamData, vertexFormat);
			var newVertexStreamList : VertexStreamList	= new VertexStreamList(newVertexStream);

			return new Mesh(newVertexStreamList, newIndexStream);
		}
	}
}

package aerys.minko.scene.node.mesh
{
	import aerys.minko.ns.minko;
	import aerys.minko.ns.minko_stream;
	import aerys.minko.scene.action.mesh.MeshAction;
	import aerys.minko.scene.node.AbstractScene;
	import aerys.minko.type.stream.IVertexStream;
	import aerys.minko.type.stream.IndexStream;
	import aerys.minko.type.stream.VertexStream;
	import aerys.minko.type.stream.VertexStreamList;
	import aerys.minko.type.vertex.format.VertexComponent;
	import aerys.minko.type.vertex.format.VertexFormat;

	public class Mesh extends AbstractScene implements IMesh
	{
		use namespace minko;
		use namespace minko_stream;

		private static var _id : uint = 0;

		protected var _vertexStream	: IVertexStream	= null;
		protected var _indexStream	: IndexStream	= null;

		public function get version() : uint
		{
			return _vertexStream.version + _indexStream.version;
		}

		public function get vertexStream() : IVertexStream
		{
			return _vertexStream;
		}

		public function set vertexStream(value : IVertexStream) : void
		{
			_vertexStream = value;
		}

		public function get indexStream() : IndexStream
		{
			return _indexStream;
		}

		public function set indexStream(value : IndexStream) : void
		{
			_indexStream = value;
		}

		public function Mesh(vertexStream	: IVertexStream	= null,
							 indexStream	: IndexStream 	= null)
		{
			super();

			_vertexStream = vertexStream;
			_indexStream = indexStream;

			initialize();
		}

		private function initialize() : void
		{
			if (!_indexStream && _vertexStream)
				_indexStream = new IndexStream(null, vertexStream.length, vertexStream.dynamic);

			actions[0] = MeshAction.meshAction;
		}

		/**
		 * Create a new copy of the Mesh object with the specified VertexFormat.
		 *
		 *

		 * If no VertexFormat is specified, the original VertexFormat of the Mesh
		 * will be used. Calling the "clone" method and specifying a VertexFormat
		 * is a good way to create a lighter Mesh by copying only the required
		 * vertex components data.
		 *

		 *
		 * @param vertexFormat
		 * @return
		 *
		 */
		public function clone(vertexFormat : VertexFormat = null) : IMesh
		{
			vertexFormat ||= _vertexStream.format.clone();

			var meshes				: Vector.<IMesh>			= Vector.<IMesh>([this])
			var mergedMesh			: IMesh						= createMesh(meshes, vertexFormat);
			return mergedMesh;
		}

		/**
		 * The "mergeMeshes" method will takes multiple Mesh objects and will
		 * merge all their data to create a single and only Mesh out of it.
		 *
		 *

		 * Because all the Mesh objects can have multiple VertexFormat, the
		 * result Mesh VertexFormat will be the biggest common set of
		 * VertexComponent of all the merged meshes.
		 *

		 *
		 * @param meshes
		 * @return
		 *
		 */
		public static function mergeMeshes(meshes : Vector.<IMesh>) : IMesh
		{
			// insersect all meshes vertexformats
			var numMeshes		: uint			= meshes.length;
			var vertexFormat	: VertexFormat	= meshes[0].vertexStream.format.clone();

			for (var meshId : uint = 1; meshId < numMeshes; ++meshId)
			{
				var mesh : IMesh = meshes[meshId];
				vertexFormat.intersectWith(mesh.vertexStream.format);
			}

			var vertexComponents	: Vector.<VertexComponent>	= vertexFormat.componentList;
			var mergedMesh			: IMesh						= createMesh(meshes, vertexFormat);

			return mergedMesh;
		}

		private static function createMesh(meshes		: Vector.<IMesh>,
										   vertexFormat	: VertexFormat) : IMesh
		{

			var newIndexStreamData 			: Vector.<uint>				= new Vector.<uint>();
			var newVertexStreamData			: Vector.<Number>			= new Vector.<Number>();

			var components					: Vector.<VertexComponent>	= vertexFormat.componentList;
			var componentCount				: uint						= components.length;
			var componentOffsets			: Vector.<uint>				= new Vector.<uint>(componentCount, true);
			var componentSizes				: Vector.<uint>				= new Vector.<uint>(componentCount, true);
			var componentDwordsPerVertex	: Vector.<uint>				= new Vector.<uint>(componentCount, true);
			var componentDatas				: Vector.<Vector.<Number>>	= new Vector.<Vector.<Number>>(componentCount, true);

			var meshCount : uint = meshes.length;
			for (var i : uint = 0; i < meshCount; ++i)
			{
				var mesh			: IMesh			= meshes[i];
				var vertexStream	: IVertexStream	= mesh.vertexStream;

				// append index data
				var indexData	: Vector.<uint>		= mesh.indexStream.getIndices();
				var indexCount	: uint				= indexData.length;
				var indexOffset	: uint				= newVertexStreamData.length / vertexFormat.dwordsPerVertex;

				for (var j : uint = 0; j < indexCount; ++j)
					newIndexStreamData.push(indexData[j] + indexOffset);

				// get offsets, sizes, and buffers for components in the current mesh.
				for (var k : uint = 0; k < componentCount; ++k)
				{
					var vertexComponent	: VertexComponent	= components[k];
					var subVertexStream	: VertexStream		= vertexStream.getSubStreamByComponent(vertexComponent);
					var subvertexFormat	: VertexFormat		= subVertexStream.format;

					componentOffsets[k]			= subvertexFormat.getOffsetForComponent(vertexComponent);
					componentDwordsPerVertex[k]	= subvertexFormat.dwordsPerVertex;
					componentSizes[k]			= vertexComponent.dwords;
					componentDatas[k]			= subVertexStream._data;
				}

				// push vertex data into the new buffer.
				var vertexCount : uint = mesh.vertexStream.length;
				for (var vertexId : uint = 0; vertexId < vertexCount; ++vertexId)
					for (var componentId : uint = 0; componentId < componentCount; ++componentId)
					{
						var vertexData		: Vector.<Number>	= componentDatas[componentId];
						var componentSize	: uint				= componentSizes[componentId];
						var componentOffset	: uint				= componentOffsets[componentId] + vertexId * componentDwordsPerVertex[componentId];
						var componentLimit	: uint				= componentSize + componentOffset;

						for (var n : uint = componentOffset; n < componentLimit; ++n)
							newVertexStreamData.push(vertexData[n]);
					}
			}

			var newIndexStream		: IndexStream		= new IndexStream(newIndexStreamData);
			var newVertexStream		: VertexStream		= new VertexStream(newVertexStreamData, vertexFormat);
			var newVertexStreamList : VertexStreamList	= new VertexStreamList(newVertexStream);

			return new Mesh(newVertexStreamList, newIndexStream);
		}
	}
}

Optimize mesh cloning and merging.

Optimize mesh cloning and merging.