package org.openforis.collect.presenter
{
	import flash.events.Event;
	import flash.net.FileFilter;

	import mx.collections.ArrayCollection;
	import mx.collections.IList;
	import mx.collections.ListCollectionView;
	import mx.controls.Tree;
	import mx.events.ListEvent;
	import mx.rpc.AsyncResponder;
	import mx.rpc.events.ResultEvent;

	import org.openforis.collect.Application;
	import org.openforis.collect.client.CSVDataImportClient;
	import org.openforis.collect.client.ClientFactory;
	import org.openforis.collect.event.UIEvent;
	import org.openforis.collect.i18n.Message;
	import org.openforis.collect.metamodel.proxy.AttributeDefinitionProxy;
	import org.openforis.collect.metamodel.proxy.EntityDefinitionProxy;
	import org.openforis.collect.metamodel.proxy.ModelVersionProxy;
	import org.openforis.collect.metamodel.proxy.NodeDefinitionProxy;
	import org.openforis.collect.model.CollectRecord$Step;
	import org.openforis.collect.model.NodeItem;
	import org.openforis.collect.ui.view.CSVDataImportView;
	import org.openforis.collect.util.AlertUtil;
	import org.openforis.collect.util.ArrayUtil;
	import org.openforis.collect.util.CollectionUtil;
	import org.openforis.collect.util.StringUtil;

	import spark.components.DropDownList;

	/**
	 *
	 * @author Ricci, Stefano
	 *
	 * */
	public class CSVDataImportPresenter extends AbstractReferenceDataImportPresenter {

		private static const ALL_STEPS_ITEM:Object = {label: Message.get('global.allItemsLabel')};
		private static const ALLOWED_MULTIPLE_FILES_IMPORT_FILE_EXTENSIONS:Array = [".zip"];

		private var _importClient:CSVDataImportClient;

		public function CSVDataImportPresenter(view:CSVDataImportView) {
			super(view, new MessageKeys());
			_importClient = ClientFactory.csvDataImportClient;
		}

		override public function init():void {
			super.init();
			updateImportFileFormatInfoMessage();
		}

		override protected function initEventListeners():void {
			super.initEventListeners();
			view.entitySelectionTree.addEventListener(ListEvent.ITEM_CLICK, entityTreeItemSelectHandler);
			view.importType.addEventListener(Event.CHANGE, importTypeChangeHandler);
		}

		override protected function createFileFilter():FileFilter {
			switch(view.importType.selectedValue) {
			case CSVDataImportView.INSERT_NEW_RECORDS_TYPE:
			case CSVDataImportView.UPDATE_EXISTING_RECORDS_TYPE:
				return new FileFilter("Excel documents", AbstractReferenceDataImportPresenter.ALLOWED_IMPORT_FILE_EXTENSIONS.join("; "));
			case CSVDataImportView.IMPORT_MULTIPLE_FILES_TYPE:
				return new FileFilter("Collect data export ZIP file", ALLOWED_MULTIPLE_FILES_IMPORT_FILE_EXTENSIONS.join("; "));
			}
		}

		protected function importTypeChangeHandler(event:Event):void {
			backToDefaultView();
			view.stepDropDownList.selectedItem = CollectRecord$Step.ENTRY;
		}

		protected function entityTreeItemSelectHandler(event:ListEvent):void {
			updateImportFileFormatInfoMessage();
		}

		protected function updateImportFileFormatInfoMessage():void {
			var messageArgs:Array = [];
			var tree:Tree = view.entitySelectionTree;
			var selectedTreeItem:NodeItem = tree.selectedItem as NodeItem;
			if ( selectedTreeItem == null ) {
				view.importFileFormatInfo = Message.get("csvDataImport.alert.selectEntity");
			} else {
				var selectedNodeDefn:EntityDefinitionProxy = selectedTreeItem.nodeDefinition as EntityDefinitionProxy;
				messageArgs[0] = StringUtil.concat(", ", getRecordKeyColumnNames(selectedNodeDefn));
				messageArgs[1] = StringUtil.concat(", ", getAncestorKeyColumnNames(selectedNodeDefn));
				messageArgs[2] = StringUtil.concat(", ", getExampleAttributeColumnNames(selectedNodeDefn));
				view.importFileFormatInfo = Message.get(messageKeys.IMPORT_FILE_FORMAT_INFO, messageArgs);
			}
		}

		private function getExampleAttributeColumnNames(selectedNodeDefn:EntityDefinitionProxy):Array {
			var result:Array = new Array();
			var children:ListCollectionView = selectedNodeDefn.childDefinitions;
			for ( var i:int = 0; i < children.length && result.length <= 3; i++) {
				var child:NodeDefinitionProxy = children.getItemAt(i) as NodeDefinitionProxy;
				if ( child is AttributeDefinitionProxy &&
						! CollectionUtil.contains(selectedNodeDefn.keyAttributeDefinitions, child, "id") ) {
					result.push(child.name);
				}
			}
			return result;
		}

		protected function getRecordKeyColumnNames(nodeDefn:EntityDefinitionProxy):Array {
			var result:Array = new Array();
			var rootEntityDefn:EntityDefinitionProxy = nodeDefn.rootEntity;
			var keyDefns:IList = rootEntityDefn.keyAttributeDefinitions;
			for each (var keyDefn:AttributeDefinitionProxy in keyDefns) {
				result.push(rootEntityDefn.name + "_" + keyDefn.name);
			}
			return result;
		}

		protected function getAncestorKeyColumnNames(nodeDefn:EntityDefinitionProxy):Array {
			var result:ArrayCollection = new ArrayCollection();
			var currentParent:EntityDefinitionProxy = nodeDefn;
			while ( currentParent != null && currentParent != nodeDefn.rootEntity ) {
				var keyDefns:IList = currentParent.keyAttributeDefinitions;
				if ( CollectionUtil.isEmpty(keyDefns) ) {
					if ( currentParent.multiple ) {
						result.addItemAt("_" + currentParent.name + "_position", 0);
					} else {
						//TODO consider it for naming following key definitions
					}
				} else {
					var colNames:Array = new Array();
					for each (var keyDefn:AttributeDefinitionProxy in keyDefns) {
						var colName:String = "";
						if ( currentParent != nodeDefn ) {
							colName += currentParent.name + "_";
						}
						colName += keyDefn.name;
						colNames.push(colName);
					}
					result.addAllAt(new ArrayCollection(colNames), 0);
				}
				currentParent = currentParent.parent;
			}
			return result.toArray();
		}

		public function getRequiredColumnNamesForSelectedEntity():Array {
			var result:Array = new Array();
			var selectedEntity:EntityDefinitionProxy = view.entitySelectionTree.selectedItem as EntityDefinitionProxy;
			if ( selectedEntity != null ) {
				ArrayUtil.addAll(result, getRecordKeyColumnNames(selectedEntity));
				ArrayUtil.addAll(result, getAncestorKeyColumnNames(selectedEntity));
			}
			return result;
		}

		private function get view():CSVDataImportView {
			return CSVDataImportView(_view);
		}

		private function get messageKeys():MessageKeys {
			return MessageKeys(_messageKeys);
		}

		override protected function fileReferenceSelectHandler(event:Event):void {
			switch(view.importType.selectedValue) {
			case CSVDataImportView.UPDATE_EXISTING_RECORDS_TYPE:
			case CSVDataImportView.IMPORT_MULTIPLE_FILES_TYPE:
				confirmUploadStart(messageKeys.CONFIRM_IMPORT);
				break;
			default:
				//when inserting new records, do not ask for confirmation
				startUpload();
			}
		}

		override protected function checkCanImport():Boolean {
			//validate form
			var importType:String = view.importType.selectedValue as String;
			if ( importType == CSVDataImportView.UPDATE_EXISTING_RECORDS_TYPE ) {
				if ( view.entitySelectionTree.selectedItem == null ) {
					AlertUtil.showError("csvDataImport.alert.selectEntity");
					return false;
				} else if ( ! NodeItem(view.entitySelectionTree.selectedItem).nodeDefinition.multiple ) {
					AlertUtil.showError("csvDataImport.alert.selectMultipleEntity");
					return false;
				} else {
					return true;
				}
			} else if ( view.formVersionDropDownList.selectedItem == null && CollectionUtil.isNotEmpty(view.formVersionDropDownList.dataProvider) ) {
				AlertUtil.showError("csvDataImport.alert.selectModelVersion");
				return false;
			} else {
				return true;
			}
		}

		override protected function performProcessStart():void {
			var responder:AsyncResponder = new AsyncResponder(startResultHandler, faultHandler);
			var transactional:Boolean = view.transactionalCheckBox.selected;
			var validateRecords:Boolean = view.validateRecordsCheckBox.selected;
			var deleteExistingEntities:Boolean = view.deleteExistingEntitiesCheckBox.selected;

			var entityId:int;
			var selectedStep:CollectRecord$Step = null;
			var insertNewRecords:Boolean;
			var newRecordModelVersion:String = null;

			switch(view.importType.selectedValue) {
			case CSVDataImportView.INSERT_NEW_RECORDS_TYPE:
				//insert new records
				insertNewRecords = true;
				entityId = Application.activeRootEntity.id;
				var version:ModelVersionProxy = view.formVersionDropDownList.selectedItem;
				newRecordModelVersion = version == null ? null: version.name;
				break;
			case CSVDataImportView.IMPORT_MULTIPLE_FILES_TYPE:
				insertNewRecords = true;
				var version:ModelVersionProxy = view.formVersionDropDownList.selectedItem;
				newRecordModelVersion = version == null ? null: version.name;
				break;
			default:
				//update existing records
				insertNewRecords = false;
				entityId = NodeItem(view.entitySelectionTree.selectedItem).id;
				var selectedStepItem:* = view.stepDropDownList.selectedItem;
				selectedStep = selectedStepItem == ALL_STEPS_ITEM ? null: selectedStepItem as CollectRecord$Step;
			}
			_importClient.start(responder, _uploadedTempFileName, entityId, selectedStep, transactional, validateRecords, insertNewRecords, newRecordModelVersion, deleteExistingEntities);
		}

		override protected function performImportCancel():void {
			var responder:AsyncResponder = new AsyncResponder(cancelResultHandler, faultHandler);
			_importClient.cancel(responder);
		}

		override protected function performCancelThenClose():void {
			var responder:AsyncResponder = new AsyncResponder(cancelResultHandler, faultHandler);
			_importClient.cancel(responder);

			function cancelResultHandler(event:ResultEvent, token:Object = null):void {
				closePopUp();
			}
		}

		override protected function updateStatus():void {
			_importClient.getStatus(_getStatusResponder);
		}

		override protected function updateViewProcessComplete():void {
			super.updateViewProcessComplete();
			//reload record summaries
			var uiEvent:UIEvent = new UIEvent(UIEvent.RELOAD_RECORD_SUMMARIES);
			eventDispatcher.dispatchEvent(uiEvent);
		}

		override protected function loadInitialData():void {
			initView();
			updateStatus();
		}

		override protected function loadSummaries():void {
			//do nothing
		}

		protected function initView():void {
			initEntitiesTree();
			initFormVersionsDropDown();
			initStepsDropDown();
			view.importType.selectedValue = CSVDataImportView.UPDATE_EXISTING_RECORDS_TYPE;
			view.transactionalCheckBox.selected = true;
			view.validateRecordsCheckBox.selected = true;
		}

		protected function initEntitiesTree():void {
			var rootEntity:EntityDefinitionProxy = Application.activeRootEntity;
			var rootNodeItem:NodeItem = NodeItem.fromNodeDef(rootEntity, true, false, false);
			var tree:Tree = view.entitySelectionTree;
			tree.dataProvider = new ArrayCollection([rootNodeItem]);
			tree.callLater(function():void {
				tree.expandItem(rootNodeItem, true);
			});
		}

		protected function initFormVersionsDropDown():void {
			Application.activeSurvey.versions;
			var items:IList = new ArrayCollection(Application.activeSurvey.versions.toArray());
			var dropDownList:DropDownList = view.formVersionDropDownList;
			dropDownList.dataProvider = items;

			//if only one model version is defined, select it
			if ( items.length == 1 ) {
				dropDownList.selectedIndex = 0;
			}
		}

		protected function initStepsDropDown():void {
			var steps:IList = new ArrayCollection(CollectRecord$Step.constants);
			steps.addItemAt(ALL_STEPS_ITEM, 0);
			var stepDropDownList:DropDownList = view.stepDropDownList;
			stepDropDownList.dataProvider = steps;
			stepDropDownList.callLater(function():void {
				stepDropDownList.selectedIndex = 0;
			});
		}

		override protected function backToDefaultView():void {
			switch(view.importType.selectedValue) {
			case CSVDataImportView.UPDATE_EXISTING_RECORDS_TYPE:
				view.currentState = CSVDataImportView.STATE_UPDATE_EXISTING_RECORDS;
				break;
			case CSVDataImportView.INSERT_NEW_RECORDS_TYPE:
				view.currentState = CSVDataImportView.STATE_INSERT_NEW_RECORDS;
				break;
			case CSVDataImportView.IMPORT_MULTIPLE_FILES_TYPE:
				view.currentState = CSVDataImportView.STATE_IMPORT_MULTIPLE_FILES;
				break;
			}
		}

	}
}
import org.openforis.collect.presenter.ReferenceDataImportMessageKeys;

class MessageKeys extends ReferenceDataImportMessageKeys {
	/*
	override public function get CONFIRM_CLOSE_TITLE():String {
		return "csvDataImport.confirmClose.title";
	}
	*/

	public function get IMPORT_FILE_FORMAT_INFO():String {
		return "csvDataImport.importFileFormatInfo";
	}

	override public function get CONFIRM_IMPORT():String {
		return "csvDataImport.confirmImport.message";
	}

}

package org.openforis.collect.presenter
{
	import flash.events.Event;
	import flash.net.FileFilter;

	import mx.collections.ArrayCollection;
	import mx.collections.IList;
	import mx.collections.ListCollectionView;
	import mx.controls.Tree;
	import mx.events.ListEvent;
	import mx.rpc.AsyncResponder;
	import mx.rpc.events.ResultEvent;

	import org.openforis.collect.Application;
	import org.openforis.collect.client.CSVDataImportClient;
	import org.openforis.collect.client.ClientFactory;
	import org.openforis.collect.event.UIEvent;
	import org.openforis.collect.i18n.Message;
	import org.openforis.collect.metamodel.proxy.AttributeDefinitionProxy;
	import org.openforis.collect.metamodel.proxy.EntityDefinitionProxy;
	import org.openforis.collect.metamodel.proxy.ModelVersionProxy;
	import org.openforis.collect.metamodel.proxy.NodeDefinitionProxy;
	import org.openforis.collect.model.CollectRecord$Step;
	import org.openforis.collect.model.NodeItem;
	import org.openforis.collect.ui.view.CSVDataImportView;
	import org.openforis.collect.util.AlertUtil;
	import org.openforis.collect.util.ArrayUtil;
	import org.openforis.collect.util.CollectionUtil;
	import org.openforis.collect.util.StringUtil;

	import spark.components.DropDownList;

	/**
	 *
	 * @author Ricci, Stefano
	 *
	 * */
	public class CSVDataImportPresenter extends AbstractReferenceDataImportPresenter {

		private static const ALL_STEPS_ITEM:Object = {label: Message.get('global.allItemsLabel')};
		private static const ALLOWED_MULTIPLE_FILES_IMPORT_FILE_EXTENSIONS:Array = [".zip"];

		private var _importClient:CSVDataImportClient;

		public function CSVDataImportPresenter(view:CSVDataImportView) {
			super(view, new MessageKeys());
			_importClient = ClientFactory.csvDataImportClient;
		}

		override public function init():void {
			super.init();
			updateImportFileFormatInfoMessage();
		}

		override protected function initEventListeners():void {
			super.initEventListeners();
			view.entitySelectionTree.addEventListener(ListEvent.ITEM_CLICK, entityTreeItemSelectHandler);
			view.importType.addEventListener(Event.CHANGE, importTypeChangeHandler);
		}

		override protected function createFileFilter():FileFilter {
			switch(view.importType.selectedValue) {
			case CSVDataImportView.IMPORT_MULTIPLE_FILES_TYPE:
				return new FileFilter("Collect data export ZIP file", ALLOWED_MULTIPLE_FILES_IMPORT_FILE_EXTENSIONS.join("; "));
			default:
				return new FileFilter("Excel documents", AbstractReferenceDataImportPresenter.ALLOWED_IMPORT_FILE_EXTENSIONS.join("; "));
			}
		}

		protected function importTypeChangeHandler(event:Event):void {
			backToDefaultView();
			view.stepDropDownList.selectedItem = CollectRecord$Step.ENTRY;
		}

		protected function entityTreeItemSelectHandler(event:ListEvent):void {
			updateImportFileFormatInfoMessage();
		}

		protected function updateImportFileFormatInfoMessage():void {
			var messageArgs:Array = [];
			var tree:Tree = view.entitySelectionTree;
			var selectedTreeItem:NodeItem = tree.selectedItem as NodeItem;
			if ( selectedTreeItem == null ) {
				view.importFileFormatInfo = Message.get("csvDataImport.alert.selectEntity");
			} else {
				var selectedNodeDefn:EntityDefinitionProxy = selectedTreeItem.nodeDefinition as EntityDefinitionProxy;
				messageArgs[0] = StringUtil.concat(", ", getRecordKeyColumnNames(selectedNodeDefn));
				messageArgs[1] = StringUtil.concat(", ", getAncestorKeyColumnNames(selectedNodeDefn));
				messageArgs[2] = StringUtil.concat(", ", getExampleAttributeColumnNames(selectedNodeDefn));
				view.importFileFormatInfo = Message.get(messageKeys.IMPORT_FILE_FORMAT_INFO, messageArgs);
			}
		}

		private function getExampleAttributeColumnNames(selectedNodeDefn:EntityDefinitionProxy):Array {
			var result:Array = new Array();
			var children:ListCollectionView = selectedNodeDefn.childDefinitions;
			for ( var i:int = 0; i < children.length && result.length <= 3; i++) {
				var child:NodeDefinitionProxy = children.getItemAt(i) as NodeDefinitionProxy;
				if ( child is AttributeDefinitionProxy &&
						! CollectionUtil.contains(selectedNodeDefn.keyAttributeDefinitions, child, "id") ) {
					result.push(child.name);
				}
			}
			return result;
		}

		protected function getRecordKeyColumnNames(nodeDefn:EntityDefinitionProxy):Array {
			var result:Array = new Array();
			var rootEntityDefn:EntityDefinitionProxy = nodeDefn.rootEntity;
			var keyDefns:IList = rootEntityDefn.keyAttributeDefinitions;
			for each (var keyDefn:AttributeDefinitionProxy in keyDefns) {
				result.push(rootEntityDefn.name + "_" + keyDefn.name);
			}
			return result;
		}

		protected function getAncestorKeyColumnNames(nodeDefn:EntityDefinitionProxy):Array {
			var result:ArrayCollection = new ArrayCollection();
			var currentParent:EntityDefinitionProxy = nodeDefn;
			while ( currentParent != null && currentParent != nodeDefn.rootEntity ) {
				var keyDefns:IList = currentParent.keyAttributeDefinitions;
				if ( CollectionUtil.isEmpty(keyDefns) ) {
					if ( currentParent.multiple ) {
						result.addItemAt("_" + currentParent.name + "_position", 0);
					} else {
						//TODO consider it for naming following key definitions
					}
				} else {
					var colNames:Array = new Array();
					for each (var keyDefn:AttributeDefinitionProxy in keyDefns) {
						var colName:String = "";
						if ( currentParent != nodeDefn ) {
							colName += currentParent.name + "_";
						}
						colName += keyDefn.name;
						colNames.push(colName);
					}
					result.addAllAt(new ArrayCollection(colNames), 0);
				}
				currentParent = currentParent.parent;
			}
			return result.toArray();
		}

		public function getRequiredColumnNamesForSelectedEntity():Array {
			var result:Array = new Array();
			var selectedEntity:EntityDefinitionProxy = view.entitySelectionTree.selectedItem as EntityDefinitionProxy;
			if ( selectedEntity != null ) {
				ArrayUtil.addAll(result, getRecordKeyColumnNames(selectedEntity));
				ArrayUtil.addAll(result, getAncestorKeyColumnNames(selectedEntity));
			}
			return result;
		}

		private function get view():CSVDataImportView {
			return CSVDataImportView(_view);
		}

		private function get messageKeys():MessageKeys {
			return MessageKeys(_messageKeys);
		}

		override protected function fileReferenceSelectHandler(event:Event):void {
			switch(view.importType.selectedValue) {
			case CSVDataImportView.UPDATE_EXISTING_RECORDS_TYPE:
			case CSVDataImportView.IMPORT_MULTIPLE_FILES_TYPE:
				confirmUploadStart(messageKeys.CONFIRM_IMPORT);
				break;
			default:
				//when inserting new records, do not ask for confirmation
				startUpload();
			}
		}

		override protected function checkCanImport():Boolean {
			//validate form
			var importType:String = view.importType.selectedValue as String;
			if ( importType == CSVDataImportView.UPDATE_EXISTING_RECORDS_TYPE ) {
				if ( view.entitySelectionTree.selectedItem == null ) {
					AlertUtil.showError("csvDataImport.alert.selectEntity");
					return false;
				} else if ( ! NodeItem(view.entitySelectionTree.selectedItem).nodeDefinition.multiple ) {
					AlertUtil.showError("csvDataImport.alert.selectMultipleEntity");
					return false;
				} else {
					return true;
				}
			} else if ( view.formVersionDropDownList.selectedItem == null && CollectionUtil.isNotEmpty(view.formVersionDropDownList.dataProvider) ) {
				AlertUtil.showError("csvDataImport.alert.selectModelVersion");
				return false;
			} else {
				return true;
			}
		}

		override protected function performProcessStart():void {
			var responder:AsyncResponder = new AsyncResponder(startResultHandler, faultHandler);
			var transactional:Boolean = view.transactionalCheckBox.selected;
			var validateRecords:Boolean = view.validateRecordsCheckBox.selected;
			var deleteExistingEntities:Boolean = view.deleteExistingEntitiesCheckBox.selected;

			var entityId:int;
			var selectedStep:CollectRecord$Step = null;
			var insertNewRecords:Boolean;
			var newRecordModelVersion:String = null;

			switch(view.importType.selectedValue) {
			case CSVDataImportView.INSERT_NEW_RECORDS_TYPE:
				//insert new records
				insertNewRecords = true;
				entityId = Application.activeRootEntity.id;
				var version:ModelVersionProxy = view.formVersionDropDownList.selectedItem;
				newRecordModelVersion = version == null ? null: version.name;
				break;
			case CSVDataImportView.IMPORT_MULTIPLE_FILES_TYPE:
				insertNewRecords = true;
				var version:ModelVersionProxy = view.formVersionDropDownList.selectedItem;
				newRecordModelVersion = version == null ? null: version.name;
				break;
			default:
				//update existing records
				insertNewRecords = false;
				entityId = NodeItem(view.entitySelectionTree.selectedItem).id;
				var selectedStepItem:* = view.stepDropDownList.selectedItem;
				selectedStep = selectedStepItem == ALL_STEPS_ITEM ? null: selectedStepItem as CollectRecord$Step;
			}
			_importClient.start(responder, _uploadedTempFileName, entityId, selectedStep, transactional, validateRecords, insertNewRecords, newRecordModelVersion, deleteExistingEntities);
		}

		override protected function performImportCancel():void {
			var responder:AsyncResponder = new AsyncResponder(cancelResultHandler, faultHandler);
			_importClient.cancel(responder);
		}

		override protected function performCancelThenClose():void {
			var responder:AsyncResponder = new AsyncResponder(cancelResultHandler, faultHandler);
			_importClient.cancel(responder);

			function cancelResultHandler(event:ResultEvent, token:Object = null):void {
				closePopUp();
			}
		}

		override protected function updateStatus():void {
			_importClient.getStatus(_getStatusResponder);
		}

		override protected function updateViewProcessComplete():void {
			super.updateViewProcessComplete();
			//reload record summaries
			var uiEvent:UIEvent = new UIEvent(UIEvent.RELOAD_RECORD_SUMMARIES);
			eventDispatcher.dispatchEvent(uiEvent);
		}

		override protected function loadInitialData():void {
			initView();
			updateStatus();
		}

		override protected function loadSummaries():void {
			//do nothing
		}

		protected function initView():void {
			initEntitiesTree();
			initFormVersionsDropDown();
			initStepsDropDown();
			view.importType.selectedValue = CSVDataImportView.UPDATE_EXISTING_RECORDS_TYPE;
			view.transactionalCheckBox.selected = true;
			view.validateRecordsCheckBox.selected = true;
		}

		protected function initEntitiesTree():void {
			var rootEntity:EntityDefinitionProxy = Application.activeRootEntity;
			var rootNodeItem:NodeItem = NodeItem.fromNodeDef(rootEntity, true, false, false);
			var tree:Tree = view.entitySelectionTree;
			tree.dataProvider = new ArrayCollection([rootNodeItem]);
			tree.callLater(function():void {
				tree.expandItem(rootNodeItem, true);
			});
		}

		protected function initFormVersionsDropDown():void {
			Application.activeSurvey.versions;
			var items:IList = new ArrayCollection(Application.activeSurvey.versions.toArray());
			var dropDownList:DropDownList = view.formVersionDropDownList;
			dropDownList.dataProvider = items;

			//if only one model version is defined, select it
			if ( items.length == 1 ) {
				dropDownList.selectedIndex = 0;
			}
		}

		protected function initStepsDropDown():void {
			var steps:IList = new ArrayCollection(CollectRecord$Step.constants);
			steps.addItemAt(ALL_STEPS_ITEM, 0);
			var stepDropDownList:DropDownList = view.stepDropDownList;
			stepDropDownList.dataProvider = steps;
			stepDropDownList.callLater(function():void {
				stepDropDownList.selectedIndex = 0;
			});
		}

		override protected function backToDefaultView():void {
			switch(view.importType.selectedValue) {
			case CSVDataImportView.UPDATE_EXISTING_RECORDS_TYPE:
				view.currentState = CSVDataImportView.STATE_UPDATE_EXISTING_RECORDS;
				break;
			case CSVDataImportView.INSERT_NEW_RECORDS_TYPE:
				view.currentState = CSVDataImportView.STATE_INSERT_NEW_RECORDS;
				break;
			case CSVDataImportView.IMPORT_MULTIPLE_FILES_TYPE:
				view.currentState = CSVDataImportView.STATE_IMPORT_MULTIPLE_FILES;
				break;
			}
		}

	}
}
import org.openforis.collect.presenter.ReferenceDataImportMessageKeys;

class MessageKeys extends ReferenceDataImportMessageKeys {
	/*
	override public function get CONFIRM_CLOSE_TITLE():String {
		return "csvDataImport.confirmClose.title";
	}
	*/

	public function get IMPORT_FILE_FORMAT_INFO():String {
		return "csvDataImport.importFileFormatInfo";
	}

	override public function get CONFIRM_IMPORT():String {
		return "csvDataImport.confirmImport.message";
	}

}

Solve compilation problem

Solve compilation problem


////////////////////////////////////////////////////////////////////////////////
//
//  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.html.beads
{

	import org.apache.flex.core.BeadViewBase;
	import org.apache.flex.core.IBead;
	import org.apache.flex.core.IBeadView;
	import org.apache.flex.core.IScrollingLayoutParent;
	import org.apache.flex.core.IStrand;
    import org.apache.flex.core.IParentIUIBase;
    import org.apache.flex.core.IParent;
    import org.apache.flex.core.IUIBase;
	import org.apache.flex.core.UIBase;
	import org.apache.flex.core.ValuesManager;
	import org.apache.flex.html.Container;
	import org.apache.flex.html.supportClasses.Border;
	import org.apache.flex.html.supportClasses.ContainerContentArea;
	import org.apache.flex.html.supportClasses.ScrollBar;
    import org.apache.flex.html.beads.models.ScrollBarModel;

    /**
     *  The ContainerView class is the default view for
     *  the org.apache.flex.html.Container class.
     *  It lets you use some CSS styles to manage the border, background
     *  and padding around the content area.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10.2
     *  @playerversion AIR 2.6
     *  @productversion FlexJS 0.0
     */
	public class ScrollingContainerView extends BeadViewBase implements IBeadView, IScrollingLayoutParent
	{
        /**
         *  Constructor.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function ScrollingContainerView()
		{
		}

        /**
         *  The actual parent that parents the children.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		protected var actualParent:UIBase;

        /**
         *  @copy org.apache.flex.core.IBead#strand
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		override public function set strand(value:IStrand):void
		{
			super.strand = value;

			var padding:Object = determinePadding();

			if (contentAreaNeeded())
			{
				actualParent = new ContainerContentArea();
				IParent(value).addElement(actualParent);
				Container(value).setActualParent(actualParent);
				actualParent.x = padding.paddingLeft;
				actualParent.y = padding.paddingTop;
			}
			else
			{
				actualParent = value as UIBase;
			}

			var backgroundColor:Object = ValuesManager.valuesImpl.getValue(value, "background-color");
			var backgroundImage:Object = ValuesManager.valuesImpl.getValue(value, "background-image");
			if (backgroundColor != null || backgroundImage != null)
			{
				if (value.getBeadByType(IBackgroundBead) == null)
					value.addBead(new (ValuesManager.valuesImpl.getValue(value, "iBackgroundBead")) as IBead);
			}

			var borderStyle:String;
			var borderStyles:Object = ValuesManager.valuesImpl.getValue(value, "border");
			if (borderStyles is Array)
			{
				borderStyle = borderStyles[1];
			}
			if (borderStyle == null)
			{
				borderStyle = ValuesManager.valuesImpl.getValue(value, "border-style") as String;
			}
			if (borderStyle != null && borderStyle != "none")
			{
				if (value.getBeadByType(IBorderBead) == null)
					value.addBead(new (ValuesManager.valuesImpl.getValue(value, "iBorderBead")) as IBead);
			}
		}

		/**
		 *  Determines the top and left padding values, if any, as set by
		 *  padding style values. This includes "padding" for all padding values
		 *  as well as "padding-left" and "padding-top".
		 *
		 *  Returns an object with paddingLeft and paddingTop properties.
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 10.2
		 *  @playerversion AIR 2.6
		 *  @productversion FlexJS 0.0
		 */
		protected function determinePadding():Object
		{
			var paddingLeft:Object;
			var paddingTop:Object;
			var padding:Object = ValuesManager.valuesImpl.getValue(_strand, "padding");
			if (padding is Array)
			{
				if (padding.length == 1)
					paddingLeft = paddingTop = padding[0];
				else if (padding.length <= 3)
				{
					paddingLeft = padding[1];
					paddingTop = padding[0];
				}
				else if (padding.length == 4)
				{
					paddingLeft = padding[3];
					paddingTop = padding[0];
				}
			}
			else if (padding == null)
			{
				paddingLeft = ValuesManager.valuesImpl.getValue(_strand, "padding-left");
				paddingTop = ValuesManager.valuesImpl.getValue(_strand, "padding-top");
			}
			else
			{
				paddingLeft = paddingTop = padding;
			}
			var pl:Number = Number(paddingLeft);
			var pt:Number = Number(paddingTop);

			return {paddingLeft:pl, paddingTop:pt};
		}

		/**
		 *  Returns true if container to create a separate ContainerContentArea.
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 10.2
		 *  @playerversion AIR 2.6
		 *  @productversion FlexJS 0.0
		 */
		protected function contentAreaNeeded():Boolean
		{
			var padding:Object = determinePadding();

			return (!isNaN(padding.paddingLeft) && padding.paddingLeft > 0 ||
				    !isNaN(padding.paddingTop) && padding.paddingTop > 0);
		}

        /**
         *  The parent of the children.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function get contentView():IParentIUIBase
		{
			return actualParent;
		}

        /**
         *  The border.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function get border():Border
		{
			return null;
		}

        /**
         *  The host component, which can resize to different slots.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function get resizableView():IUIBase
		{
			return _strand as IUIBase;
		}

        private var _vScrollBar:ScrollBar;

        /**
         *  The vertical ScrollBar, if it exists.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function get vScrollBar():ScrollBar
		{
            if (!_vScrollBar)
                _vScrollBar = createScrollBar();
            return _vScrollBar;
		}

        /**
         *  The horizontal ScrollBar, if it exists.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function get hScrollBar():ScrollBar
		{
			return null;
		}

        /**
         * @private
         */
        private function createScrollBar():ScrollBar
        {
            var vsb:ScrollBar;
            vsb = new ScrollBar();
            var vsbm:ScrollBarModel = new ScrollBarModel();
            vsbm.maximum = 100;
            vsbm.minimum = 0;
            vsbm.pageSize = 10;
            vsbm.pageStepSize = 10;
            vsbm.snapInterval = 1;
            vsbm.stepSize = 1;
            vsbm.value = 0;
            vsb.model = vsbm;
            vsb.width = 16;
            IParent(_strand).addElement(vsb);
            return vsb;
        }

        /**
         *  The position of the vertical scrollbar
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
        public function get verticalScrollPosition():Number
        {
            return ScrollBarModel(vScrollBar.model).value;
        }

        /**
         *  @private
         */
        public function set verticalScrollPosition(value:Number):void
        {
            ScrollBarModel(vScrollBar.model).value = value;
        }

        /**
         *  The maximum position of the vertical scrollbar
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
        public function get maxVerticalScrollPosition():Number
        {
            return ScrollBarModel(vScrollBar.model).maximum -
                ScrollBarModel(vScrollBar.model).pageSize;
        }

    }
}

////////////////////////////////////////////////////////////////////////////////
//
//  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.html.beads
{

	import org.apache.flex.core.BeadViewBase;
	import org.apache.flex.core.IBead;
	import org.apache.flex.core.IBeadView;
	import org.apache.flex.core.IParent;
	import org.apache.flex.core.IParentIUIBase;
	import org.apache.flex.core.IScrollingLayoutParent;
	import org.apache.flex.core.IStrand;
	import org.apache.flex.core.IUIBase;
	import org.apache.flex.core.UIBase;
	import org.apache.flex.core.ValuesManager;
    import org.apache.flex.events.Event;
	import org.apache.flex.html.Container;
	import org.apache.flex.html.beads.models.ScrollBarModel;
	import org.apache.flex.html.supportClasses.Border;
	import org.apache.flex.html.supportClasses.ContainerContentArea;
	import org.apache.flex.html.supportClasses.ScrollBar;

    /**
     *  The ContainerView class is the default view for
     *  the org.apache.flex.html.Container class.
     *  It lets you use some CSS styles to manage the border, background
     *  and padding around the content area.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10.2
     *  @playerversion AIR 2.6
     *  @productversion FlexJS 0.0
     */
	public class ScrollingContainerView extends BeadViewBase implements IBeadView, IScrollingLayoutParent
	{
        /**
         *  Constructor.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function ScrollingContainerView()
		{
		}

        /**
         *  The actual parent that parents the children.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		protected var actualParent:UIBase;

        /**
         *  @copy org.apache.flex.core.IBead#strand
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		override public function set strand(value:IStrand):void
		{
			super.strand = value;
            var host:UIBase = value as UIBase;
            if (host.numChildren > 0)
                childHandler(null);
            else
                host.addEventListener("childrenAdded", childHandler);
        }

        private function childHandler(event:Event):void
        {
            var host:UIBase = _strand as UIBase;
            if (host.numChildren > 0)
            {
                actualParent = host.getChildAt(0) as UIBase;
            }

			var backgroundColor:Object = ValuesManager.valuesImpl.getValue(host, "background-color");
			var backgroundImage:Object = ValuesManager.valuesImpl.getValue(host, "background-image");
			if (backgroundColor != null || backgroundImage != null)
			{
				if (host.getBeadByType(IBackgroundBead) == null)
                    host.addBead(new (ValuesManager.valuesImpl.getValue(host, "iBackgroundBead")) as IBead);
			}

			var borderStyle:String;
			var borderStyles:Object = ValuesManager.valuesImpl.getValue(host, "border");
			if (borderStyles is Array)
			{
				borderStyle = borderStyles[1];
			}
			if (borderStyle == null)
			{
				borderStyle = ValuesManager.valuesImpl.getValue(host, "border-style") as String;
			}
			if (borderStyle != null && borderStyle != "none")
			{
				if (host.getBeadByType(IBorderBead) == null)
                    host.addBead(new (ValuesManager.valuesImpl.getValue(host, "iBorderBead")) as IBead);
			}
		}

        /**
         *  The parent of the children.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function get contentView():IParentIUIBase
		{
			return actualParent;
		}

        /**
         *  The border.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function get border():Border
		{
			return null;
		}

        /**
         *  The host component, which can resize to different slots.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function get resizableView():IUIBase
		{
			return _strand as IUIBase;
		}

        private var _vScrollBar:ScrollBar;

        /**
         *  The vertical ScrollBar, if it exists.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function get vScrollBar():ScrollBar
		{
            if (!_vScrollBar)
                _vScrollBar = createScrollBar();
            return _vScrollBar;
		}

        /**
         *  The horizontal ScrollBar, if it exists.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function get hScrollBar():ScrollBar
		{
			return null;
		}

        /**
         * @private
         */
        private function createScrollBar():ScrollBar
        {
            var vsb:ScrollBar;
            vsb = new ScrollBar();
            var vsbm:ScrollBarModel = new ScrollBarModel();
            vsbm.maximum = 100;
            vsbm.minimum = 0;
            vsbm.pageSize = 10;
            vsbm.pageStepSize = 10;
            vsbm.snapInterval = 1;
            vsbm.stepSize = 1;
            vsbm.value = 0;
            vsb.model = vsbm;
            vsb.width = 16;
            IParent(_strand).addElement(vsb);
            return vsb;
        }

        /**
         *  The position of the vertical scrollbar
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
        public function get verticalScrollPosition():Number
        {
            return ScrollBarModel(vScrollBar.model).value;
        }

        /**
         *  @private
         */
        public function set verticalScrollPosition(value:Number):void
        {
            ScrollBarModel(vScrollBar.model).value = value;
        }

        /**
         *  The maximum position of the vertical scrollbar
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
        public function get maxVerticalScrollPosition():Number
        {
            return ScrollBarModel(vScrollBar.model).maximum -
                ScrollBarModel(vScrollBar.model).pageSize;
        }

    }
}

rewrite to assume a single child as the content area


package aerys.minko.render.shader
{
	import aerys.minko.ns.minko_shader;
	import aerys.minko.render.shader.compiler.graph.nodes.AbstractNode;
	import aerys.minko.render.shader.compiler.graph.nodes.leaf.Constant;
	import aerys.minko.render.shader.compiler.graph.nodes.vertex.Extract;
	import aerys.minko.render.shader.compiler.graph.nodes.vertex.Instruction;
	import aerys.minko.render.shader.compiler.graph.nodes.vertex.Overwriter;
	import aerys.minko.render.shader.compiler.register.Components;
	import aerys.minko.type.math.Matrix4x4;

	import flash.utils.Proxy;
	import flash.utils.flash_proxy;

	/**
	 * SFloat (Shader Float) objects are GPU-side computed value proxies
	 * declared, defined and used in ActionScript shaders.
	 *
	 *

	 * ActionScript shaders define what operations will be
	 * performed on the GPU. Those operations take arguments and return
	 * values that will be computed and accessible on the graphics hardware
	 * only. Those values are represented by SFloat objects.
	 *

	 *
	 *

	 * Because SFloat objects are just hardware memory proxies, it is not
	 * possible (and does not make sense to try) to read their actual value
	 * using CPU-side code. For the very same reason, most of the errors will
	 * be detected at runtime only. The only available property is the size
	 * (number of components) of the corresponding value (ie. 3D vector
	 * operations will return SFloat objects of size 3, dot-product will
	 * return a scalar SFloat object of size 1, ...).
	 *

	 *
	 *

	 * SFloat objects also provide OOP shader programming by encapsulating
	 * common operations (add, multiply, ...). They also allow the use of
	 * dynamic properties in order to read or write sub-components of
	 * non-scalar values. Example:
	 *

	 *
	 *

	 * public function getOutputColor() : void
	 * {
	 * 	var diffuse : SFloat = sampleTexture(BasicStyle.DIFFUSE_MAP);
	 *
	 * 	// use the RGB components of the diffuse map but use a
	 * 	// fixed alpha = 0.5
	 *  return combine(diffuse.rgb, 0.5);
	 * }
	 *

	 *
	 *

	 * Each SFloat object wraps a shader graph node that will be evaluated
	 * by the compiler in order to create the corresponding AGAL bytecode.
	 *

	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public dynamic final class SFloat extends Proxy
	{
		use namespace minko_shader;

		minko_shader var _node	: AbstractNode	= null;

		public final function get size() : uint
		{
			return _node.size;
		}

		public function SFloat(value : Object)
		{
			_node = getNode(value);
		}

		public final function scaleBy(arg : Object) : SFloat
		{
			_node = new Instruction(Instruction.MUL, _node, getNode(arg));

			return this;
		}

		public final function incrementBy(value : Object) : SFloat
		{
			_node = new Instruction(Instruction.ADD, _node, getNode(value));

			return this;
		}

		public final function decrementBy(value : Object) : SFloat
		{
			_node = new Instruction(Instruction.SUB, _node, getNode(value));

			return this;
		}

		public final function normalize() : SFloat
		{
			_node = new Instruction(Instruction.NRM, _node);

			return this;
		}

		public final function negate() : SFloat
		{
			_node = new Instruction(Instruction.NEG, _node);

			return this;
		}

		override flash_proxy function getProperty(name : *) : *
		{
			return new SFloat(new Extract(_node, Components.stringToComponent(name)));
		}

		override flash_proxy function setProperty(name : *, value : *) : void
		{
			var propertyName		: String		= String(name);
			var propertyComponent	: uint			= getPropertyWriteComponentMask(propertyName);
			var propertyNode		: AbstractNode	= getNode(value);
			var nodeComponent		: uint 			= Components.createContinuous(0, 0, _node.size, _node.size);

			propertyNode = new Overwriter(
				new <AbstractNode>[propertyNode],
				new <uint>[Components.createContinuous(0, 0, 4, propertyNode.size)]
			);

			_node = new Overwriter(
				new <AbstractNode>[_node, propertyNode],
				new <uint>[nodeComponent, propertyComponent]
			);
		}

		public static function getPropertyWriteComponentMask(string : String) : uint
		{
			var result : uint = 0x04040404;

			for (var i : uint = 0; i < 4; ++i)
				if (i < string.length)
					switch (string.charAt(i))
					{
						case 'x': case 'X': case 'r': case 'R':
							result = (0xffffff00 & result) | i;
							result |= i << (8 * 0);
							break;

						case 'y': case 'Y': case 'g': case 'G':
							result = (0xffff00ff & result) | i << 8;
							break;

						case 'z': case 'Z': case 'b': case 'B':
							result = (0xff00ffff & result) | i << 16;
							break;

						case 'w': case 'W': case 'a': case 'A':
							result = (0x00ffffff & result) | i << 24;
							break;

						default:
							throw new Error('Invalid string.');
					}

			return result;
		}

		private function getNode(value : Object) : AbstractNode
		{
			if (value is AbstractNode)
				return value as AbstractNode;

			if (value is SFloat)
				return (value as SFloat)._node;

			if (value is uint || value is int || value is Number)
				return new Constant(new <Number>[Number(value)]);

			if (value is Matrix4x4)
				return new Constant(Matrix4x4(value).getRawData(null, 0, false));

			throw new Error('This type cannot be casted to a shader value.');
		}
	}
}

package aerys.minko.render.shader
{
	import aerys.minko.ns.minko_shader;
	import aerys.minko.render.shader.compiler.graph.nodes.AbstractNode;
	import aerys.minko.render.shader.compiler.graph.nodes.leaf.Constant;
	import aerys.minko.render.shader.compiler.graph.nodes.vertex.Extract;
	import aerys.minko.render.shader.compiler.graph.nodes.vertex.Instruction;
	import aerys.minko.render.shader.compiler.graph.nodes.vertex.Overwriter;
	import aerys.minko.render.shader.compiler.register.Components;
	import aerys.minko.type.math.Matrix4x4;

	import flash.utils.Proxy;
	import flash.utils.flash_proxy;

	/**
	 * SFloat (Shader Float) objects are GPU-side computed value proxies
	 * declared, defined and used in ActionScript shaders.
	 *
	 *

	 * ActionScript shaders define what operations will be
	 * performed on the GPU. Those operations take arguments and return
	 * values that will be computed and accessible on the graphics hardware
	 * only. Those values are represented by SFloat objects.
	 *

	 *
	 *

	 * Because SFloat objects are just hardware memory proxies, it is not
	 * possible (and does not make sense to try) to read their actual value
	 * using CPU-side code. For the very same reason, most of the errors will
	 * be detected at runtime only. The only available property is the size
	 * (number of components) of the corresponding value (ie. 3D vector
	 * operations will return SFloat objects of size 3, dot-product will
	 * return a scalar SFloat object of size 1, ...).
	 *

	 *
	 *

	 * SFloat objects also provide OOP shader programming by encapsulating
	 * common operations (add, multiply, ...). They also allow the use of
	 * dynamic properties in order to read or write sub-components of
	 * non-scalar values. Example:
	 *

	 *
	 *

	 * public function getOutputColor() : void
	 * {
	 * 	var diffuse : SFloat = sampleTexture(BasicStyle.DIFFUSE_MAP);
	 *
	 * 	// use the RGB components of the diffuse map but use a
	 * 	// fixed alpha = 0.5
	 *  return combine(diffuse.rgb, 0.5);
	 * }
	 *

	 *
	 *

	 * Each SFloat object wraps a shader graph node that will be evaluated
	 * by the compiler in order to create the corresponding AGAL bytecode.
	 *

	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public dynamic final class SFloat extends Proxy
	{
		use namespace minko_shader;

		minko_shader var _node	: AbstractNode	= null;

		public final function get size() : uint
		{
			return _node.size;
		}

		public function SFloat(value : Object)
		{
			_node = getNode(value);
		}

		public final function scaleBy(arg : Object) : SFloat
		{
			_node = new Instruction(Instruction.MUL, _node, getNode(arg));

			return this;
		}

		public final function incrementBy(value : Object) : SFloat
		{
			_node = new Instruction(Instruction.ADD, _node, getNode(value));

			return this;
		}

		public final function decrementBy(value : Object) : SFloat
		{
			_node = new Instruction(Instruction.SUB, _node, getNode(value));

			return this;
		}

		public final function normalize() : SFloat
		{
			_node = new Instruction(Instruction.NRM, _node);

			return this;
		}

		public final function negate() : SFloat
		{
			_node = new Instruction(Instruction.NEG, _node);

			return this;
		}

		override flash_proxy function getProperty(name : *) : *
		{
			return new SFloat(new Extract(_node, Components.stringToComponent(name)));
		}

		override flash_proxy function setProperty(name : *, value : *) : void
		{
			var propertyName		: String		= String(name);
			var propertyComponent	: uint			= getPropertyWriteComponentMask(propertyName);
			var propertyNode		: AbstractNode	= getNode(value);
			var nodeComponent		: uint 			= Components.createContinuous(0, 0, _node.size, _node.size);

			propertyNode = new Overwriter(
				new <AbstractNode>[propertyNode],
				new <uint>[Components.createContinuous(0, 0, 4, propertyNode.size)]
			);

			_node = new Overwriter(
				new <AbstractNode>[_node, propertyNode],
				new <uint>[nodeComponent, propertyComponent]
			);
		}

		private function getPropertyWriteComponentMask(string : String) : uint
		{
			var result : uint = 0x04040404;

			for (var i : uint = 0; i < 4; ++i)
				if (i < string.length)
					switch (string.charAt(i))
					{
						case 'x': case 'X': case 'r': case 'R':
							result = (0xffffff00 & result) | i;
							result |= i << (8 * 0);
							break;

						case 'y': case 'Y': case 'g': case 'G':
							result = (0xffff00ff & result) | i << 8;
							break;

						case 'z': case 'Z': case 'b': case 'B':
							result = (0xff00ffff & result) | i << 16;
							break;

						case 'w': case 'W': case 'a': case 'A':
							result = (0x00ffffff & result) | i << 24;
							break;

						default:
							throw new Error('Invalid string.');
					}

			return result;
		}

		private function getNode(value : Object) : AbstractNode
		{
			if (value is AbstractNode)
				return value as AbstractNode;

			if (value is SFloat)
				return (value as SFloat)._node;

			if (value is uint || value is int || value is Number)
				return new Constant(new <Number>[Number(value)]);

			if (value is Matrix4x4)
				return new Constant(Matrix4x4(value).getRawData(null, 0, false));

			throw new Error('This type cannot be casted to a shader value.');
		}
	}
}

set SFloat.getPropertyWriteComponentMask() to be private


package aerys.minko.type.animation
{
	import aerys.minko.scene.node.IScene;
	import aerys.minko.scene.node.ITransformable;
	import aerys.minko.scene.node.group.Group;
	import aerys.minko.type.animation.timeline.ITimeline;

	import flash.utils.getTimer;

	public class Animation
	{
		private var _id				: String;

		private var _beginTime		: uint;
		private var _lastTime		: uint;

		private var _playingOn		: IScene;
		private var _timelines		: Vector.<ITimeline>;
		private var _duration		: uint;

		public function get id() : String { return _id; }

		public function Animation(id		: String,
								  timelines : Vector.<ITimeline>)
		{
			_id			= id;
			_timelines	= timelines;
			_lastTime	= 0;

			_duration	= 0;
			for each (var timeline : ITimeline in timelines)
				if (_duration < timeline.duration)
					_duration = timeline.duration;
		}

		public function tick() : void
		{
			var time : uint = (getTimer() - _beginTime) % _duration;
			transformNodes(time);
		}

		public function step(deltaTime : uint = 80) : void
		{
			var time : uint = (_lastTime + deltaTime) % _duration;
			transformNodes(time);
		}

		public function stepReverse(deltaTime : uint = 80) : void
		{
			var time : int = (_lastTime - deltaTime) % _duration;
			if (time < 0)
				time = time + _duration;

			transformNodes(time);
		}

		private function transformNodes(time : uint) : void
		{
			_lastTime = time;

			var timelinesCount : uint = _timelines.length;
			for (var i : uint = 0; i < timelinesCount; ++i)
			{
				var timeline		: ITimeline	= _timelines[i];
				var timelineTarget	: String	= timeline.target;

				var target			: ITransformable;
				if (_playingOn.name == timelineTarget)
					target = ITransformable(_playingOn);
				else if (_playingOn is Group)
					target = ITransformable(Group(_playingOn).getDescendantByName(timelineTarget));

				if (!target)
					continue;

				timeline.updateAt(time, target);
			}
		}

		public function playOn(node : IScene) : void
		{
			_beginTime	= getTimer();
			_playingOn	= node;
			_lastTime	= 0;
		}

		public function stop() : void
		{
			_playingOn = null;
		}

	}
}

package aerys.minko.type.animation
{
	import aerys.minko.scene.node.IScene;
	import aerys.minko.scene.node.ITransformable;
	import aerys.minko.scene.node.group.Group;
	import aerys.minko.type.animation.timeline.ITimeline;

	import flash.utils.getTimer;

	public class Animation
	{
		private var _id				: String;

		private var _beginTime		: uint;
		private var _lastTime		: uint;

		private var _playingOn		: IScene;
		private var _timelines		: Vector.<ITimeline>;
		private var _duration		: uint;

		public function get id() : String { return _id; }

		public function Animation(id		: String,
								  timelines : Vector.<ITimeline>)
		{
			_id			= id;
			_timelines	= timelines;
			_lastTime	= 0;

			_duration	= 0;
			for each (var timeline : ITimeline in timelines)
				if (_duration < timeline.duration)
					_duration = timeline.duration;
		}

		public function tick() : void
		{
			var time : uint = (getTimer() - _beginTime) % _duration;
			transformNodes(time);
		}

		public function step(deltaTime : int = 80) : void
		{
			var time : int;

			if (deltaTime > 0)
			{
				time = (_lastTime + deltaTime) % _duration;
			}
			else
			{
				time = (_lastTime + deltaTime) % _duration;
				if (time < 0)
					time = time + _duration;
			}

			transformNodes(time);
		}

		private function transformNodes(time : uint) : void
		{
			_lastTime = time;

			var timelinesCount : uint = _timelines.length;
			for (var i : uint = 0; i < timelinesCount; ++i)
			{
				var timeline		: ITimeline	= _timelines[i];
				var timelineTarget	: String	= timeline.target;

				var target			: ITransformable;
				if (_playingOn.name == timelineTarget)
					target = ITransformable(_playingOn);
				else if (_playingOn is Group)
					target = ITransformable(Group(_playingOn).getDescendantByName(timelineTarget));

				if (!target)
					continue;

				timeline.updateAt(time, target);
			}
		}

		public function playOn(node : IScene) : void
		{
			_beginTime	= getTimer();
			_playingOn	= node;
			_lastTime	= 0;
		}

		public function stop() : void
		{
			_playingOn = null;
		}
	}
}

Delete Animation::stepReverse method

Delete Animation::stepReverse method


package aerys.minko.scene
{
	import aerys.minko.scene.node.Group;
	import aerys.minko.scene.node.ISceneNode;
	import aerys.minko.type.binding.DataBindings;

	import flash.utils.Dictionary;
	import flash.utils.Proxy;
	import flash.utils.describeType;
	import flash.utils.flash_proxy;
	import flash.utils.getQualifiedClassName;

	public dynamic class SceneIterator extends Proxy
	{
		private static const TYPE_CACHE	: Dictionary		= new Dictionary(true);
		private static const OPERATORS	: Vector.<String>	= new <String>[
			'//', '/', '[', ']', '..', '.', '~=', '?=', '=', '@', '*', '(', ')',
			'>=', '>', '<=', '<', '==', '='
		];
		private static const REGEX_TRIM	: RegExp			= /^\s+|\s+$/;

		private var _path		: String				= null;
		private var _selection	: Vector.<ISceneNode>	= null;
		private var _modifier	: String				= null;

		public function get length() : uint
		{
			return _selection.length;
		}

		public function SceneIterator(path 		: String,
									  selection : Vector.<ISceneNode>,
									  modifier	: String	= "")
		{
			super();

			_modifier = modifier;

			initialize(path, selection);
		}

		public function toString() : String
		{
			return _selection.toString();
		}

//		override flash_proxy function setProperty(name : *, value : *):void
//		{
//			var propertyName : String = name;
//
//			for each (var node : ISceneNode in _selection)
//				getValueObject(node, _modifier)[propertyName] = value;
//		}

		override flash_proxy function getProperty(name : *) : *
		{
			var index : int = parseInt(name);

			if (index == name)
				return index < _selection.length ? _selection[index] : null;
			else
			{
				throw new Error(
					'Unable to get a property on a set of objects. '
					+ 'You must use the [] operator to fetch one of the objects.'
				);
			}
		}

		override flash_proxy function nextNameIndex(index : int) : int
		{
			return index < _selection.length ? index + 1 : 0;
		}

		override flash_proxy function nextName(index : int) : String
		{
			return String(index - 1);
		}

		override flash_proxy function nextValue(index : int) : *
		{
			return _selection[int(index - 1)];
		}

//		override flash_proxy function callProperty(name:*, ...parameters):*
//		{
//			var methodName : String = name;
//
//			for each (var node : ISceneNode in _selection)
//			{
//				var method : Function = getValueObject(node, _modifier)[methodName];
//
//				method.apply(null, parameters);
//			}
//
//			return this;
//		}

		private function initialize(path : String, selection : Vector.<ISceneNode>) : void
		{
			_path = path;

			// update root
			var token : String = getToken();

			_selection = selection.slice();
			if (token == "/")
			{
				selectRoots();
				nextToken(token);
			}

			// parse
			while ((token = getToken()) != null)
			{
				switch (token)
				{
					case '//' :
						nextToken(token);
						selectDescendants();
						break ;
					case '/' :
						nextToken(token);
						selectChildren();
						break ;
					default :
						nextToken(token);
						parseNodeType(token);
						break ;
				}
			}
		}

		private function getToken(doNext : Boolean = false) : String
		{
			var token	: String	= null;

			if (!_path)
				return null;

			_path = _path.replace(/^\s+/, '');

			var nextOpIndex : int = int.MAX_VALUE;

			for each (var op : String in OPERATORS)
			{
				var opIndex : int = _path.indexOf(op);

				if (opIndex > 0 && opIndex < nextOpIndex)
					nextOpIndex = opIndex;

				if (opIndex == 0)
				{
					token = op;
					break ;
				}
			}

			if (!token)
				token = _path.substring(0, nextOpIndex);

			if (doNext)
				nextToken(token);

			return token;
		}

		private function getValueToken() : Object
		{
			var value : Object	= null;

			_path = _path.replace(/^\s+/, '');

			if (_path.charAt(0) == "'")
			{
				var endOfStringIndex : int = _path.indexOf("'", 1);

				if (endOfStringIndex < 0)
					throw new Error("Unterminated string expression.");

				var stringValue	: String	=  _path.substring(1, endOfStringIndex);

				_path = _path.substring(endOfStringIndex + 1);

				value = stringValue;
			}
			else
			{
				var token : String	= getToken(true);

				if (token == 'true')
					value = true;
				else if (token == 'false')
					value = false;
				else if (token.indexOf('0x') == 0)
					value = parseInt(token, 16);
			}

			return value;
		}

		private function nextToken(token : String) : void
		{
			_path = _path.substring(_path.indexOf(token) + token.length);
		}

		private function selectChildren(typeName : String = null) : void
		{
			var selection : Vector.<ISceneNode> = _selection.slice();

			if (typeName != null)
				typeName = typeName.toLowerCase();

			_selection.length = 0;
			for each (var node : ISceneNode in selection)
			{
				if (node is Group)
				{
					var group 		: Group = node as Group;
					var numChildren : uint 	= group.numChildren;

					for (var i : uint = 0; i < numChildren; ++i)
					{
						var child 		: ISceneNode 	= group.getChildAt(i);
						var className	: String		= getQualifiedClassName(child)
						var childType 	: String 		= className.substr(className.lastIndexOf(':') + 1);

						if (typeName == null || childType.toLowerCase() == typeName)
							_selection.push(child);
					}
				}
			}
		}

		private function selectRoots() : void
		{
			var selection	: Vector.<ISceneNode>	= _selection.slice();

			_selection.length = 0;
			for each (var node : ISceneNode in selection)
				if (_selection.indexOf(node.root) < 0)
					_selection.push(node);
		}

		private function selectDescendants() : void
		{
			var selection : Vector.<ISceneNode> = _selection.slice();

			_selection.length = 0;
			for each (var node : ISceneNode in selection)
			{
				_selection.push(node);
				if (node is Group)
					(node as Group).getDescendantsByType(ISceneNode, _selection);
			}
		}

		private function selectParents() : void
		{
			var selection : Vector.<ISceneNode> = _selection.slice();

			_selection.length = 0;
			for each (var node : ISceneNode in selection)
			{
				if (node.parent)
					_selection.push(node.parent);
				else
					_selection.push(node);
			}
		}

		private function parseNodeType(nodeType : String) : void
		{
			if (nodeType == '.')
			{
				// nothing
			}
			if (nodeType == '..')
				selectParents();
			else if (nodeType == '*')
				selectChildren();
			else
				selectChildren(nodeType);

			// apply predicates
			var token : String = getToken();

			while (token == '[')
			{
				nextToken(token);
				parsePredicate();

				token = getToken();
			}
		}

		private function parsePredicate() : void
		{
			var propertyName	: String	= getToken(true);
			var isBinding		: Boolean	= propertyName == '@';

			if (isBinding)
				propertyName = getToken(true);

			var index	: int	= parseInt(propertyName);

			if (propertyName == 'hasController')
				filterOnController();
			else if (propertyName == 'hasProperty')
				filterOnProperty();
			else if (propertyName == 'position')
				filterOnPosition();
			else if (propertyName == 'last')
				filterLast();
			else if (propertyName == 'first')
				filterFirst();
			else if (index.toString() == propertyName)
			{
				if (index < _selection.length)
				{
					_selection[0] = _selection[index];
					_selection.length = 1;
				}
				else
					_selection.length = 0;
			}
			else
				filterOnValue(propertyName, isBinding);

			checkNextToken(']');
		}

		private function filterLast() : void
		{
			checkNextToken('(');
			checkNextToken(')');

			_selection[0] = _selection[uint(_selection.length - 1)];
			_selection.length = 1;
		}

		private function filterFirst() : void
		{
			checkNextToken('(');
			checkNextToken(')');

			_selection.length = 1;
		}

		private function filterOnValue(propertyName : String, isBinding : Boolean = false) : void
		{
			var operator	: String	= getToken(true);
			var chunks		: Array		= [propertyName];

			while (operator == '.')
			{
				chunks.push(getToken(true));
				operator = getToken(true);
			}

			var value		: Object	= getValueToken();
			var numNodes	: uint		= _selection.length;

			for (var i : int = numNodes - 1; i >= 0; --i)
			{
				var node		: ISceneNode	= _selection[i];
				var nodeValue 	: Object 		= null;

				if (isBinding && (node['bindings'] is DataBindings))
					nodeValue = (node['bindings'] as DataBindings).getProperty(propertyName);
				else
				{
					try
					{
						nodeValue = getValueObject(node, chunks);
						if (!compare(operator, nodeValue, value))
							removeFromSelection(i);
					}
					catch (e : Error)
					{
						removeFromSelection(i);
					}
				}

			}
		}

		private function compare(operator : String, a : Object, b : Object) : Boolean
		{
			switch (operator)
			{
				case '>' :
					return a > b;
				case '>=' :
					return a >= b;
				case '<' :
					return a >= b;
				case '<=' :
					return a <= b;
				case '=' :
				case '==' :
					return a == b;
				case '~=' :
					var matches	: Array	= String(a).match(b);

					return matches && matches.length != 0;
				default:
					throw new Error('Unknown comparison operator \'' + operator + '\'');
			}
		}

		private function filterOnController() : Object
		{
			checkNextToken('(');
			var controllerName : String = getToken(true);
			checkNextToken(')');

			var numNodes	: uint	= _selection.length;

			for (var i : int = numNodes - 1; i >= 0; --i)
			{
				var node			: ISceneNode	= _selection[i];
				var numControllers	: uint			= node.numControllers;
				var keepSceneNode	: Boolean		= false;

				for (var controllerId : uint = 0; controllerId < numControllers; ++controllerId)
				{
					var controllerType : String = getQualifiedClassName(node.getController(controllerId));
					controllerType = controllerType.substr(controllerType.lastIndexOf(':') + 1);

					if (controllerType == controllerName)
					{
						keepSceneNode = true;
						break;
					}
				}

				if (!keepSceneNode)
					removeFromSelection(i);
			}

			return null;
		}

		private function filterOnPosition() : void
		{
			checkNextToken('(');
			checkNextToken(')');

			var operator 	: String 	= getToken(true);
			var value 		: uint 		= uint(parseInt(getToken(true)));

			switch (operator)
			{
				case '>':
					++value;
				case '>=':
					_selection = _selection.slice(value);
					break;
				case '<':
					--value;
				case '<=':
					_selection = _selection.slice(0, value);
					break;
				case '=':
				case '==':
					_selection[0] = _selection[value];
					_selection.length = 1;
				default:
					throw new Error('Unknown comparison operator \'' + operator + '\'');
			}
		}

		private function filterOnProperty() : void
		{
			checkNextToken('(');

			var chunks		: Array 	= [getToken(true)];
			var operator	: String 	= getToken(true);

			while (operator == '.')
			{
				chunks.push(operator);
				operator = getToken(true);
			}

			if (operator != ')')
				throwParseError(')', operator);

			var numNodes : uint	= _selection.length;
			for (var i : int = numNodes - 1; i >= 0; --i)
			{
				try
				{
					getValueObject(_selection[i], chunks);
				}
				catch (e : Error)
				{
					removeFromSelection(i);
				}
			}
		}

		private function getValueObject(source : Object, chunks : Array) : Object
		{
			if (chunks)
				for each (var chunk : String in chunks)
					source = source[chunk];

			return source;
		}

		private function removeFromSelection(index : uint) : void
		{
			var numNodes : uint = _selection.length - 1;

			_selection[index] = _selection[numNodes];
			_selection.length = numNodes;
		}

		private function checkNextToken(expected : String) : void
		{
			var token : String = getToken(true);

			if (token != expected)
				throwParseError(expected, token);
		}

		private function throwParseError(expected	: String,
										 got		: String) : void
		{
			throw new Error(
				'Parse error: expected \'' + expected + '\', got \'' + got + '\'.'
			);
		}
	}
}

package aerys.minko.scene
{
	import aerys.minko.scene.node.Group;
	import aerys.minko.scene.node.ISceneNode;
	import aerys.minko.type.binding.DataBindings;

	import flash.utils.Dictionary;
	import flash.utils.Proxy;
	import flash.utils.describeType;
	import flash.utils.flash_proxy;
	import flash.utils.getQualifiedClassName;

	public dynamic class SceneIterator extends Proxy
	{
		private static const TYPE_CACHE	: Dictionary		= new Dictionary(true);
		private static const OPERATORS	: Vector.<String>	= new <String>[
			'//', '/', '[', ']', '..', '.', '~=', '?=', '=', '@', '*', '(', ')',
			'>=', '>', '<=', '<', '==', '='
		];
		private static const REGEX_TRIM	: RegExp			= /^\s+|\s+$/;

		private var _path		: String				= null;
		private var _selection	: Vector.<ISceneNode>	= null;
		private var _modifier	: String				= null;

		public function get length() : uint
		{
			return _selection.length;
		}

		public function SceneIterator(path 		: String,
									  selection : Vector.<ISceneNode>,
									  modifier	: String	= "")
		{
			super();

			_modifier = modifier;

			initialize(path, selection);
		}

		public function toString() : String
		{
			return _selection.toString();
		}

//		override flash_proxy function setProperty(name : *, value : *):void
//		{
//			var propertyName : String = name;
//
//			for each (var node : ISceneNode in _selection)
//				getValueObject(node, _modifier)[propertyName] = value;
//		}

		override flash_proxy function getProperty(name : *) : *
		{
			var index : int = parseInt(name);

			if (index == name)
				return index < _selection.length ? _selection[index] : null;
			else
			{
				throw new Error(
					'Unable to get a property on a set of objects. '
					+ 'You must use the [] operator to fetch one of the objects.'
				);
			}
		}

		override flash_proxy function nextNameIndex(index : int) : int
		{
			return index < _selection.length ? index + 1 : 0;
		}

		override flash_proxy function nextName(index : int) : String
		{
			return String(index - 1);
		}

		override flash_proxy function nextValue(index : int) : *
		{
			return _selection[int(index - 1)];
		}

//		override flash_proxy function callProperty(name:*, ...parameters):*
//		{
//			var methodName : String = name;
//
//			for each (var node : ISceneNode in _selection)
//			{
//				var method : Function = getValueObject(node, _modifier)[methodName];
//
//				method.apply(null, parameters);
//			}
//
//			return this;
//		}

		private function initialize(path : String, selection : Vector.<ISceneNode>) : void
		{
			_path = path;

			// update root
			var token : String = getToken();

			_selection = selection.slice();
			if (token == "/")
			{
				selectRoots();
				nextToken(token);
			}

			// parse
			while ((token = getToken()) != null)
			{
				switch (token)
				{
					case '//' :
						nextToken(token);
						selectDescendants();
						break ;
					case '/' :
						nextToken(token);
						selectChildren();
						break ;
					default :
						nextToken(token);
						parseNodeType(token);
						break ;
				}
			}
		}

		private function getToken(doNext : Boolean = false) : String
		{
			var token	: String	= null;

			if (!_path)
				return null;

			_path = _path.replace(/^\s+/, '');

			var nextOpIndex : int = int.MAX_VALUE;

			for each (var op : String in OPERATORS)
			{
				var opIndex : int = _path.indexOf(op);

				if (opIndex > 0 && opIndex < nextOpIndex)
					nextOpIndex = opIndex;

				if (opIndex == 0)
				{
					token = op;
					break ;
				}
			}

			if (!token)
				token = _path.substring(0, nextOpIndex);

			if (doNext)
				nextToken(token);

			return token;
		}

		private function getValueToken() : Object
		{
			var value : Object	= null;

			_path = _path.replace(/^\s+/, '');

			if (_path.charAt(0) == "'")
			{
				var endOfStringIndex : int = _path.indexOf("'", 1);

				if (endOfStringIndex < 0)
					throw new Error("Unterminated string expression.");

				var stringValue	: String	=  _path.substring(1, endOfStringIndex);

				_path = _path.substring(endOfStringIndex + 1);

				value = stringValue;
			}
			else
			{
				var token : String	= getToken(true);

				if (token == 'true')
					value = true;
				else if (token == 'false')
					value = false;
				else if (token.indexOf('0x') == 0)
					value = parseInt(token, 16);
			}

			return value;
		}

		private function nextToken(token : String) : void
		{
			_path = _path.substring(_path.indexOf(token) + token.length);
		}

		private function selectChildren(typeName : String = null) : void
		{
			var selection : Vector.<ISceneNode> = _selection.slice();

			if (typeName != null)
				typeName = typeName.toLowerCase();

			_selection.length = 0;
			for each (var node : ISceneNode in selection)
			{
				if (node is Group)
				{
					var group 		: Group = node as Group;
					var numChildren : uint 	= group.numChildren;

					for (var i : uint = 0; i < numChildren; ++i)
					{
						var child 		: ISceneNode 	= group.getChildAt(i);
						var className	: String		= getQualifiedClassName(child)
						var childType 	: String 		= className.substr(className.lastIndexOf(':') + 1);

						if (typeName == null || childType.toLowerCase() == typeName)
							_selection.push(child);
					}
				}
			}
		}

		private function selectRoots() : void
		{
			var selection	: Vector.<ISceneNode>	= _selection.slice();

			_selection.length = 0;
			for each (var node : ISceneNode in selection)
				if (_selection.indexOf(node.root) < 0)
					_selection.push(node);
		}

		private function selectDescendants() : void
		{
			var selection : Vector.<ISceneNode> = _selection.slice();

			_selection.length = 0;
			for each (var node : ISceneNode in selection)
			{
				_selection.push(node);
				if (node is Group)
					(node as Group).getDescendantsByType(ISceneNode, _selection);
			}
		}

		private function selectParents() : void
		{
			var selection : Vector.<ISceneNode> = _selection.slice();

			_selection.length = 0;
			for each (var node : ISceneNode in selection)
			{
				if (node.parent)
					_selection.push(node.parent);
				else
					_selection.push(node);
			}
		}

		private function parseNodeType(nodeType : String) : void
		{
			if (nodeType == '.')
			{
				// nothing
			}
			if (nodeType == '..')
				selectParents();
			else if (nodeType == '*')
				selectChildren();
			else
				selectChildren(nodeType);

			// apply predicates
			var token : String = getToken();

			while (token == '[')
			{
				nextToken(token);
				parsePredicate();

				token = getToken();
			}
		}

		private function parsePredicate() : void
		{
			var propertyName	: String	= getToken(true);
			var isBinding		: Boolean	= propertyName == '@';

			if (isBinding)
				propertyName = getToken(true);

			var index	: int	= parseInt(propertyName);

			if (propertyName == 'hasController')
				filterOnController();
			else if (propertyName == 'hasProperty')
				filterOnProperty();
			else if (propertyName == 'position')
				filterOnPosition();
			else if (propertyName == 'last')
				filterLast();
			else if (propertyName == 'first')
				filterFirst();
			else if (index.toString() == propertyName)
			{
				if (index < _selection.length)
				{
					_selection[0] = _selection[index];
					_selection.length = 1;
				}
				else
					_selection.length = 0;
			}
			else
				filterOnValue(propertyName, isBinding);

			checkNextToken(']');
		}

		private function filterLast() : void
		{
			checkNextToken('(');
			checkNextToken(')');

			if (_selection.length)
			{
				_selection[0] = _selection[uint(_selection.length - 1)];
				_selection.length = 1;
			}
		}

		private function filterFirst() : void
		{
			checkNextToken('(');
			checkNextToken(')');

			_selection.length = 1;
		}

		private function filterOnValue(propertyName : String, isBinding : Boolean = false) : void
		{
			var operator	: String	= getToken(true);
			var chunks		: Array		= [propertyName];

			while (operator == '.')
			{
				chunks.push(getToken(true));
				operator = getToken(true);
			}

			var value		: Object	= getValueToken();
			var numNodes	: uint		= _selection.length;

			for (var i : int = numNodes - 1; i >= 0; --i)
			{
				var node		: ISceneNode	= _selection[i];
				var nodeValue 	: Object 		= null;

				if (isBinding && (node['bindings'] is DataBindings))
					nodeValue = (node['bindings'] as DataBindings).getProperty(propertyName);
				else
				{
					try
					{
						nodeValue = getValueObject(node, chunks);
						if (!compare(operator, nodeValue, value))
							removeFromSelection(i);
					}
					catch (e : Error)
					{
						removeFromSelection(i);
					}
				}

			}
		}

		private function compare(operator : String, a : Object, b : Object) : Boolean
		{
			switch (operator)
			{
				case '>' :
					return a > b;
				case '>=' :
					return a >= b;
				case '<' :
					return a >= b;
				case '<=' :
					return a <= b;
				case '=' :
				case '==' :
					return a == b;
				case '~=' :
					var matches	: Array	= String(a).match(b);

					return matches && matches.length != 0;
				default:
					throw new Error('Unknown comparison operator \'' + operator + '\'');
			}
		}

		private function filterOnController() : Object
		{
			checkNextToken('(');
			var controllerName : String = getToken(true);
			checkNextToken(')');

			var numNodes	: uint	= _selection.length;

			for (var i : int = numNodes - 1; i >= 0; --i)
			{
				var node			: ISceneNode	= _selection[i];
				var numControllers	: uint			= node.numControllers;
				var keepSceneNode	: Boolean		= false;

				for (var controllerId : uint = 0; controllerId < numControllers; ++controllerId)
				{
					var controllerType : String = getQualifiedClassName(node.getController(controllerId));
					controllerType = controllerType.substr(controllerType.lastIndexOf(':') + 1);

					if (controllerType == controllerName)
					{
						keepSceneNode = true;
						break;
					}
				}

				if (!keepSceneNode)
					removeFromSelection(i);
			}

			return null;
		}

		private function filterOnPosition() : void
		{
			checkNextToken('(');
			checkNextToken(')');

			var operator 	: String 	= getToken(true);
			var value 		: uint 		= uint(parseInt(getToken(true)));

			switch (operator)
			{
				case '>':
					++value;
				case '>=':
					_selection = _selection.slice(value);
					break;
				case '<':
					--value;
				case '<=':
					_selection = _selection.slice(0, value);
					break;
				case '=':
				case '==':
					_selection[0] = _selection[value];
					_selection.length = 1;
				default:
					throw new Error('Unknown comparison operator \'' + operator + '\'');
			}
		}

		private function filterOnProperty() : void
		{
			checkNextToken('(');

			var chunks		: Array 	= [getToken(true)];
			var operator	: String 	= getToken(true);

			while (operator == '.')
			{
				chunks.push(operator);
				operator = getToken(true);
			}

			if (operator != ')')
				throwParseError(')', operator);

			var numNodes : uint	= _selection.length;
			for (var i : int = numNodes - 1; i >= 0; --i)
			{
				try
				{
					getValueObject(_selection[i], chunks);
				}
				catch (e : Error)
				{
					removeFromSelection(i);
				}
			}
		}

		private function getValueObject(source : Object, chunks : Array) : Object
		{
			if (chunks)
				for each (var chunk : String in chunks)
					source = source[chunk];

			return source;
		}

		private function removeFromSelection(index : uint) : void
		{
			var numNodes : uint = _selection.length - 1;

			_selection[index] = _selection[numNodes];
			_selection.length = numNodes;
		}

		private function checkNextToken(expected : String) : void
		{
			var token : String = getToken(true);

			if (token != expected)
				throwParseError(expected, token);
		}

		private function throwParseError(expected	: String,
										 got		: String) : void
		{
			throw new Error(
				'Parse error: expected \'' + expected + '\', got \'' + got + '\'.'
			);
		}
	}
}

fix 'last()' xpath special function 'out of bounds' exception when the selection set is empty


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 package org.mangui.hls.demux {

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

    /** Constants and utilities for the H264 video format. **/
    public class Nalu {
        /** H264 NAL unit names. **/
        private static const NAMES : Array = ['Unspecified',                  // 0
        'NDR',                          // 1
        'Partition A',                  // 2
        'Partition B',                  // 3
        'Partition C',                  // 4
        'IDR',                          // 5
        'SEI',                          // 6
        'SPS',                          // 7
        'PPS',                          // 8
        'AUD',                          // 9
        'End of Sequence',              // 10
        'End of Stream',                // 11
        'Filler Data'// 12
        ];

        /** Return an array with NAL delimiter indexes. **/
        public static function getNALU(nalu : ByteArray, position : uint) : Vector.<VideoFrame> {
            var units : Vector.<VideoFrame> = new Vector.<VideoFrame>();
            var unit_start : int;
            var unit_type : int;
            var unit_header : int;
            // Loop through data to find NAL startcodes.
            var window : uint = 0;
            nalu.position = position;
            while (nalu.bytesAvailable > 4) {
                window = nalu.readUnsignedInt();
                // Match four-byte startcodes
                if ((window & 0xFFFFFFFF) == 0x01) {
                    // push previous NAL unit if new start delimiter found, dont push unit with type = 0
                    if (unit_start && unit_type) {
                        units.push(new VideoFrame(unit_header, nalu.position - 4 - unit_start, unit_start, unit_type));
                    }
                    unit_header = 4;
                    unit_start = nalu.position;
                    unit_type = nalu.readByte() & 0x1F;
                    // NDR or IDR NAL unit
                    if (unit_type == 1 || unit_type == 5) {
                        break;
                    }
                    // Match three-byte startcodes
                } else if ((window & 0xFFFFFF00) == 0x100) {
                    // push previous NAL unit if new start delimiter found, dont push unit with type = 0
                    if (unit_start && unit_type) {
                        units.push(new VideoFrame(unit_header, nalu.position - 4 - unit_start, unit_start, unit_type));
                    }
                    nalu.position--;
                    unit_header = 3;
                    unit_start = nalu.position;
                    unit_type = nalu.readByte() & 0x1F;
                    // NDR or IDR NAL unit
                    if (unit_type == 1 || unit_type == 5) {
                        break;
                    }
                } else {
                    nalu.position -= 3;
                }
            }
            // Append the last NAL to the array.
            if (unit_start) {
                units.push(new VideoFrame(unit_header, nalu.length - unit_start, unit_start, unit_type));
            }
            // Reset position and return results.
            CONFIG::LOGGING {
            if (HLSSettings.logDebug2) {
                if (units.length) {
                    var txt : String = "AVC: ";
                    for (var i : int = 0; i < units.length; i++) {
                        txt += NAMES[units[i].type] + ", ";
                    }
                    Log.debug2(txt.substr(0, txt.length - 2) + " slices");
                } else {
                    Log.debug2('AVC: no NALU slices found');
                }
            }
            }
            nalu.position = position;
            return units;
        };
    }
}

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 package org.mangui.hls.demux {

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

    /** Constants and utilities for the H264 video format. **/
    public class Nalu {
        /** Return an array with NAL delimiter indexes. **/
        public static function getNALU(nalu : ByteArray, position : uint) : Vector.<VideoFrame> {
            var units : Vector.<VideoFrame> = new Vector.<VideoFrame>();
            var unit_start : int;
            var unit_type : int;
            var unit_header : int;
            // Loop through data to find NAL startcodes.
            var window : uint = 0;
            nalu.position = position;
            while (nalu.bytesAvailable > 4) {
                window = nalu.readUnsignedInt();
                // Match four-byte startcodes
                if ((window & 0xFFFFFFFF) == 0x01) {
                    // push previous NAL unit if new start delimiter found, dont push unit with type = 0
                    if (unit_start && unit_type) {
                        units.push(new VideoFrame(unit_header, nalu.position - 4 - unit_start, unit_start, unit_type));
                    }
                    unit_header = 4;
                    unit_start = nalu.position;
                    unit_type = nalu.readByte() & 0x1F;
                    // NDR or IDR NAL unit
                    if (unit_type == 1 || unit_type == 5) {
                        break;
                    }
                    // Match three-byte startcodes
                } else if ((window & 0xFFFFFF00) == 0x100) {
                    // push previous NAL unit if new start delimiter found, dont push unit with type = 0
                    if (unit_start && unit_type) {
                        units.push(new VideoFrame(unit_header, nalu.position - 4 - unit_start, unit_start, unit_type));
                    }
                    nalu.position--;
                    unit_header = 3;
                    unit_start = nalu.position;
                    unit_type = nalu.readByte() & 0x1F;
                    // NDR or IDR NAL unit
                    if (unit_type == 1 || unit_type == 5) {
                        break;
                    }
                } else {
                    nalu.position -= 3;
                }
            }
            // Append the last NAL to the array.
            if (unit_start) {
                units.push(new VideoFrame(unit_header, nalu.length - unit_start, unit_start, unit_type));
            }
            // Reset position and return results.
            CONFIG::LOGGING {
            if (HLSSettings.logDebug2) {
                /** H264 NAL unit names. **/
                const NAMES : Array = ['Unspecified',// 0
                'NDR',                          // 1
                'Partition A',                  // 2
                'Partition B',                  // 3
                'Partition C',                  // 4
                'IDR',                          // 5
                'SEI',                          // 6
                'SPS',                          // 7
                'PPS',                          // 8
                'AUD',                          // 9
                'End of Sequence',              // 10
                'End of Stream',                // 11
                'Filler Data'// 12
                ];
                if (units.length) {
                    var txt : String = "AVC: ";
                    for (var i : int = 0; i < units.length; i++) {
                        txt += NAMES[units[i].type] + ", ";
                    }
                    Log.debug2(txt.substr(0, txt.length - 2) + " slices");
                } else {
                    Log.debug2('AVC: no NALU slices found');
                }
            }
            }
            nalu.position = position;
            return units;
        };
    }
}

reduce release swf size

reduce release swf size


package org.mangui.osmf.plugins
{
  import flash.net.NetStream;
  import flash.net.NetConnection;
  import org.osmf.net.NetLoader;
  import org.osmf.media.URLResource;

  import org.mangui.HLS.HLS;

	public class HLSNetLoader extends NetLoader
	{
	  private var _hls:HLS;
		public function HLSNetLoader(hls:HLS)
		{
			_hls = hls;
			super();
		}

		override protected function createNetStream(connection:NetConnection, resource:URLResource):NetStream
		{
			return _hls.stream;
		}
  }
}

package org.mangui.osmf.plugins
{
  import flash.net.NetStream;
  import flash.net.NetConnection;
  import org.osmf.net.NetLoader;
  import org.osmf.media.URLResource;

  import org.mangui.HLS.HLS;

	public class HLSNetLoader extends NetLoader
	{
	  private var _hls:HLS;
    private var _connection:NetConnection;
    private var _resource:URLResource;
		public function HLSNetLoader(hls:HLS)
		{
			_hls = hls;
			super();
		}

		override protected function createNetStream(connection:NetConnection, resource:URLResource):NetStream
		{
      _connection = connection;
      _resource = resource;
			return _hls.stream;
		}
  }
}

remove warnings

remove warnings


////////////////////////////////////////////////////////////////////////////////
//
//  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.html.beads
{
	import flash.display.Graphics;

	import org.apache.flex.core.IBead;
	import org.apache.flex.core.IStrand;
	import org.apache.flex.core.UIBase;
	import org.apache.flex.core.ValuesManager;
	import org.apache.flex.events.Event;
	import org.apache.flex.events.IEventDispatcher;

    /**
     *  The SolidBackgroundBead class draws a solid filled background.
     *  The color and opacity can be specified in CSS.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10.2
     *  @playerversion AIR 2.6
     *  @productversion FlexJS 0.0
     */
	public class SolidBackgroundBead implements IBead, IBackgroundBead, IGraphicsDrawing
	{
        /**
         *  Constructor.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function SolidBackgroundBead()
		{
		}

		private var _strand:IStrand;

        /**
         *  @copy org.apache.flex.core.IBead#strand
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function set strand(value:IStrand):void
		{
			_strand = value;
            IEventDispatcher(value).addEventListener("heightChanged", changeHandler);
            IEventDispatcher(value).addEventListener("widthChanged", changeHandler);

			var bgColor:Object = ValuesManager.valuesImpl.getValue(value, "background-color");
			if( bgColor != null ) {
				backgroundColor = uint(bgColor);
			}

			var bgAlpha:Object = ValuesManager.valuesImpl.getValue(value, "opacity");
			if( bgAlpha != null ) {
				opacity = Number(bgAlpha);
			}
		}

		private var _backgroundColor:uint;

        /**
         *  The background color
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function get backgroundColor():uint
		{
			return _backgroundColor;
		}

        /**
         *  @private
         */
		public function set backgroundColor(value:uint):void
		{
			_backgroundColor = value;
			if (_strand)
				changeHandler(null);
		}

		private var _opacity:Number = 1.0;

        /**
         *  The opacity (alpha).
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function get opacity():Number
		{
			return _opacity;
		}

        /**
         *  @private
         */
		public function set opacity(value:Number):void
		{
			_opacity = value;
			if( _strand )
				changeHandler(null);
		}

		private function changeHandler(event:Event):void
		{
            var host:UIBase = UIBase(_strand);
            var g:Graphics = host.graphics;
            var w:Number = host.width;
            var h:Number = host.height;

			var gd:IGraphicsDrawing = _strand.getBeadByType(IGraphicsDrawing) as IGraphicsDrawing;
			if( this == gd ) g.clear();

            g.beginFill(backgroundColor,opacity);
            g.drawRect(0, 0, w, h);
            g.endFill();
		}
	}
}

////////////////////////////////////////////////////////////////////////////////
//
//  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.html.beads
{
    import flash.display.Sprite;
	import flash.display.Graphics;

	import org.apache.flex.core.IBead;
    import org.apache.flex.core.IBeadView;
	import org.apache.flex.core.IStrand;
	import org.apache.flex.core.IUIBase;
	import org.apache.flex.core.ValuesManager;
	import org.apache.flex.events.Event;
	import org.apache.flex.events.IEventDispatcher;

    /**
     *  The SolidBackgroundBead class draws a solid filled background.
     *  The color and opacity can be specified in CSS.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10.2
     *  @playerversion AIR 2.6
     *  @productversion FlexJS 0.0
     */
	public class SolidBackgroundBead implements IBead, IBackgroundBead, IGraphicsDrawing
	{
        /**
         *  Constructor.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function SolidBackgroundBead()
		{
		}

		private var _strand:IStrand;

        private var host:IUIBase;

        /**
         *  @copy org.apache.flex.core.IBead#strand
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function set strand(value:IStrand):void
		{
			_strand = value;
            if (value is IUIBase)
                host = IUIBase(value);
            else if (value is IBeadView)
                host = IUIBase(IBeadView(value).host);

            IEventDispatcher(host).addEventListener("heightChanged", changeHandler);
            IEventDispatcher(host).addEventListener("widthChanged", changeHandler);

			var bgColor:Object = ValuesManager.valuesImpl.getValue(host, "background-color");
			if( bgColor != null ) {
				backgroundColor = ValuesManager.valuesImpl.convertColor(bgColor);
			}

			var bgAlpha:Object = ValuesManager.valuesImpl.getValue(host, "opacity");
			if( bgAlpha != null ) {
				opacity = Number(bgAlpha);
			}
		}

		private var _backgroundColor:uint;

        /**
         *  The background color
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function get backgroundColor():uint
		{
			return _backgroundColor;
		}

        /**
         *  @private
         */
		public function set backgroundColor(value:uint):void
		{
			_backgroundColor = value;
			if (_strand)
				changeHandler(null);
		}

		private var _opacity:Number = 1.0;

        /**
         *  The opacity (alpha).
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function get opacity():Number
		{
			return _opacity;
		}

        /**
         *  @private
         */
		public function set opacity(value:Number):void
		{
			_opacity = value;
			if( _strand )
				changeHandler(null);
		}

		private function changeHandler(event:Event):void
		{
            var g:Graphics = Sprite(host).graphics;
            var w:Number = host.width;
            var h:Number = host.height;

			var gd:IGraphicsDrawing = _strand.getBeadByType(IGraphicsDrawing) as IGraphicsDrawing;
			if( this == gd ) g.clear();

            g.beginFill(backgroundColor,opacity);
            g.drawRect(0, 0, w, h);
            g.endFill();
		}
	}
}

allow as bead of view as well as component