You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flex.apache.org by ah...@apache.org on 2018/07/31 17:13:30 UTC

[07/21] git commit: [flex-examples] [refs/heads/develop] - TDF with modules where possible

http://git-wip-us.apache.org/repos/asf/flex-examples/blob/9343d391/tourdeflexmodules/src/spark/layouts/CustomLayoutFlickrWheelExample.mxml
----------------------------------------------------------------------
diff --git a/tourdeflexmodules/src/spark/layouts/CustomLayoutFlickrWheelExample.mxml b/tourdeflexmodules/src/spark/layouts/CustomLayoutFlickrWheelExample.mxml
new file mode 100644
index 0000000..238553a
--- /dev/null
+++ b/tourdeflexmodules/src/spark/layouts/CustomLayoutFlickrWheelExample.mxml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+  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.
+
+-->
+<s:Module  xmlns:fx="http://ns.adobe.com/mxml/2009"
+                xmlns:s="library://ns.adobe.com/flex/spark"
+                xmlns:mx="library://ns.adobe.com/flex/mx"
+                xmlns:my="*" minWidth="600" minHeight="350" 
+				creationComplete="requestPhotos()" backgroundColor="0x323232">
+
+     <fx:Script>
+        <![CDATA[
+            import mx.collections.ArrayCollection;
+            import mx.rpc.events.ResultEvent;
+            
+            import spark.components.Group;
+            import spark.components.supportClasses.GroupBase;
+            import spark.effects.animation.MotionPath;
+
+            [Bindable]
+            private var photoFeed:ArrayCollection;
+            
+            private function requestPhotos():void {
+                var params:Object = new Object();
+                params.format = 'rss_200_enc';
+                params.tags = searchTerms.text;
+                photoService.send(params);
+            }
+
+            private function photoHandler(event:ResultEvent):void {
+                photoFeed = event.result.rss.channel.item as ArrayCollection;
+            }
+         ]]>
+    </fx:Script>
+
+    <fx:Declarations>
+        <s:HTTPService id="photoService"
+            url="http://api.flickr.com/services/feeds/photos_public.gne"
+            result="photoHandler(event)" />
+    </fx:Declarations>
+    
+	<s:Panel title="Image Wheel Sample" width="100%" height="100%">
+	    <s:layout>
+	        <s:VerticalLayout paddingTop="10" paddingLeft="10" paddingRight="10" paddingBottom="10"/>
+	    </s:layout>
+		
+		<s:HGroup verticalAlign="middle">
+			<s:Label text="Flickr tags or search terms:" color="0xFFFFFF"/>
+			<s:TextInput id="searchTerms"
+				enter="requestPhotos()" text="bugs" />
+			<s:Button label="Search" 
+				click="requestPhotos()" />
+			<!-- The slider to control the axis angle -->
+			<s:HSlider id="axisSlider" minimum="-90" maximum="90"
+					   value="10" liveDragging="true" width="300"/>
+		</s:HGroup>
+	
+		<s:List width="100%" height="100%"
+			dataProvider="{photoFeed}"
+			itemRenderer="FlickrThumbnail"
+	        id="theList">
+			
+			<s:layout>
+			    <my:WheelLayout gap="20" axisAngle="{axisSlider.value}"/>
+			</s:layout>
+		</s:List>
+	</s:Panel>
+   
+</s:Module>

http://git-wip-us.apache.org/repos/asf/flex-examples/blob/9343d391/tourdeflexmodules/src/spark/layouts/CustomLayoutFlowExample.mxml
----------------------------------------------------------------------
diff --git a/tourdeflexmodules/src/spark/layouts/CustomLayoutFlowExample.mxml b/tourdeflexmodules/src/spark/layouts/CustomLayoutFlowExample.mxml
new file mode 100644
index 0000000..52b313d
--- /dev/null
+++ b/tourdeflexmodules/src/spark/layouts/CustomLayoutFlowExample.mxml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+  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.
+
+-->
+<s:Module  xmlns:fx="http://ns.adobe.com/mxml/2009"
+				xmlns:s="library://ns.adobe.com/flex/spark"
+				xmlns:mx="library://ns.adobe.com/flex/halo"
+				xmlns:my="*">
+	
+	<s:Panel width="100%" height="100%" title="Custom Layout - Flow Layout" >
+		<s:layout>
+			<s:VerticalLayout horizontalAlign="center"
+							  paddingTop="10" gap="10"/>
+		</s:layout>
+		
+		<!-- The drop-down to select vertical alignment -->
+		<s:HGroup verticalAlign="middle">
+			<s:Label text="Vertical align"/>
+			<s:DropDownList id="vAlign" requireSelection="true" color="0x000000">
+				<s:ArrayCollection>
+					<fx:String>bottom</fx:String>
+					<fx:String>middle</fx:String>
+					<fx:String>top</fx:String>
+				</s:ArrayCollection>
+			</s:DropDownList>                         
+		</s:HGroup>
+		
+		<!-- The drop-down to select vertical alignment -->                         
+		<s:HGroup verticalAlign="middle">
+			<s:Label text="Horizontal align"/>
+			<s:DropDownList id="hAlign" requireSelection="true">
+				<s:ArrayCollection>
+					<fx:String>left</fx:String>
+					<fx:String>center</fx:String>
+					<fx:String>right</fx:String>
+				</s:ArrayCollection>
+			</s:DropDownList>                         
+		</s:HGroup>
+		
+		<!-- The slider to control the list width -->
+		<s:HGroup verticalAlign="bottom">
+			<s:Label text="Container width"/>
+			<s:HSlider id="widthSlider" minimum="10" maximum="400"
+					   value="200" liveDragging="true"/>
+		</s:HGroup>
+		
+		<!-- The slider to control the horizontal gap -->
+		<s:HGroup verticalAlign="bottom">
+			<s:Label text="Horizontal gap"/>
+			<s:HSlider id="hGapSlider" minimum="0" maximum="50"
+					   value="10" liveDragging="true"/>
+		</s:HGroup>
+		
+		<!-- A Spark List -->
+		<s:List id="list1" width="{widthSlider.value}" height="112"
+				selectedIndex="7"
+				dataProvider="{new ArrayCollection(
+				'The quick fox jumped over the lazy sleepy\n\dog'.split(' '))}">
+			
+			<!-- Configure the layout to be the FlowLayout -->    
+			<s:layout>
+				<my:FlowLayout1 horizontalAlign="{hAlign.selectedItem}"
+								verticalAlign="{vAlign.selectedItem}"
+								horizontalGap="{hGapSlider.value}"/>
+			</s:layout>
+		</s:List>
+	</s:Panel>
+
+</s:Module>
+

http://git-wip-us.apache.org/repos/asf/flex-examples/blob/9343d391/tourdeflexmodules/src/spark/layouts/CustomLayoutHBaselineExample.mxml
----------------------------------------------------------------------
diff --git a/tourdeflexmodules/src/spark/layouts/CustomLayoutHBaselineExample.mxml b/tourdeflexmodules/src/spark/layouts/CustomLayoutHBaselineExample.mxml
new file mode 100644
index 0000000..6d8ff1a
--- /dev/null
+++ b/tourdeflexmodules/src/spark/layouts/CustomLayoutHBaselineExample.mxml
@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+  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.
+
+-->
+<!-- http://evtimmy.com/2010/02/extending-horizontallayout-to-support-baseline-align-to-text/ -->
+<s:Module xmlns:fx="http://ns.adobe.com/mxml/2009" 
+			   xmlns:s="library://ns.adobe.com/flex/spark" 
+			   xmlns:mx="library://ns.adobe.com/flex/mx" 
+			   xmlns:local="*">
+	<fx:Script>
+		<![CDATA[
+			import mx.events.FlexEvent;
+			
+			protected function update(event:Event):void
+			{
+				globalBaseline.top = theLayout.actualBaseline;
+				checkBoxBaseline.top = checkBox.y + checkBox.baselinePosition;
+				labelBaseline.top = label.y + label.baselinePosition;
+				labelBaseline.left = label.x;
+				buttonBaseline.top = button.y + button.baselinePosition; 
+				buttonBaseline.left = button.x;
+			}
+			
+		]]>
+	</fx:Script>
+	
+	<s:Panel width="100%" height="100%" title="CustomLayout with HBaselineLayout">
+		
+		<!-- Controls -->
+		<s:HGroup left="14" top="5">
+			<s:VGroup>
+				<s:CheckBox label="Checkbox" id="showCheckBox" selected="true"/>
+				<s:CheckBox label="Label" id="showLabel" selected="true"/>
+				<s:CheckBox label="Button " id="showButton" selected="true"/>
+				<s:CheckBox label="Layout " id="showLayout" selected="true"/>
+			</s:VGroup>
+			
+			<s:TileGroup requestedColumnCount="2">
+				<s:CheckBox id="baseline1Check" label="Offset Checkbox baseline" selected="true"/>
+				<s:HSlider id="baseline1Slider" minimum="-100" maximum="100" enabled="{baseline1Check.selected}" width="160"/>
+				<s:CheckBox id="baseline2Check" label="Offset Label baseline" selected="true"/>
+				<s:HSlider id="baseline2Slider" minimum="-100" maximum="100" enabled="{baseline2Check.selected}" width="160"/>
+				<s:CheckBox id="baseline3Check" label="Offset Button baseline" selected="true"/>
+				<s:HSlider id="baseline3Slider" minimum="-100" maximum="100" enabled="{baseline3Check.selected}" width="160"/>
+				<s:CheckBox id="baseline4Check" label="Offset Layout baseline"/>
+				<s:HSlider id="baseline4Slider" minimum="-100" maximum="100" enabled="{baseline4Check.selected}" width="160" value="15"/>
+				<s:CheckBox id="buttonHeightCheck" label="Override Button height" selected="true"/>
+				<s:HSlider id="buttonHeightSlider" minimum="21" maximum="150" enabled="{buttonHeightCheck.selected}" width="160" value="21"/>
+			</s:TileGroup>
+		</s:HGroup>
+		
+		
+		<s:Group id="container" updateComplete="update(event)" top="138" horizontalCenter="0">
+			<s:layout>
+				<local:HBaselineLayout id="theLayout" verticalAlign="baseline"
+									   globalBaseline="{baseline4Check.selected ? baseline4Slider.value : NaN}"/>
+			</s:layout>
+			<s:CheckBox id="checkBox" label="One check box" move="update(event)"
+						baseline="{baseline1Check.selected ? baseline1Slider.value : 0}"/>
+			<s:Label id="label" text="...and some random text..." move="update(event)"
+					 baseline="{baseline2Check.selected ? baseline2Slider.value : 0}"/>
+			<s:Button id="button" label="and a Button!" move="update(event)"
+					  height="{buttonHeightCheck.selected ? buttonHeightSlider.value : 21}"
+					  baseline="{baseline3Check.selected ? baseline3Slider.value : 0}"/>
+			<!-- visual guides for the baselines -->
+			<s:Group includeInLayout="false">
+				<s:Line width="{container.width}" id="globalBaseline" top="{theLayout.actualBaseline}"
+						visible="{showLayout.selected}">
+					<s:stroke>
+						<s:SolidColorStroke color="0x00FF00" weight="2"/>
+					</s:stroke>
+				</s:Line>
+				
+				<s:Line width="{checkBox.width-1}" id="checkBoxBaseline"
+						visible="{showCheckBox.selected}">
+					<s:stroke>
+						<s:SolidColorStroke color="0xFF0000" alpha="0.5" weight="2"/>
+					</s:stroke>
+				</s:Line>
+				
+				<s:Line width="{label.width-1}" id="labelBaseline"
+						visible="{showLabel.selected}">
+					<s:stroke>
+						<s:SolidColorStroke color="0x0000FF" alpha="0.5" weight="2"/>
+					</s:stroke>
+				</s:Line>
+				
+				<s:Line width="{button.width-1}" id="buttonBaseline"
+						visible="{showButton.selected}">
+					<s:stroke>
+						<s:SolidColorStroke color="0xFF00FF" alpha="0.5" weight="2"/>
+					</s:stroke>
+				</s:Line>
+			</s:Group>
+		</s:Group>
+		<s:Label right="14" top="7" color="0x323232" width="200"
+				 text="This sample shows how you can create a custom layout to extend the HorizontalLayout to provide
+				 baseline alignment functionality."/>
+	</s:Panel>
+	
+</s:Module>

http://git-wip-us.apache.org/repos/asf/flex-examples/blob/9343d391/tourdeflexmodules/src/spark/layouts/FlickrThumbnail.mxml
----------------------------------------------------------------------
diff --git a/tourdeflexmodules/src/spark/layouts/FlickrThumbnail.mxml b/tourdeflexmodules/src/spark/layouts/FlickrThumbnail.mxml
new file mode 100644
index 0000000..91c690d
--- /dev/null
+++ b/tourdeflexmodules/src/spark/layouts/FlickrThumbnail.mxml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+  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.
+
+-->
+<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
+                xmlns:s="library://ns.adobe.com/flex/spark"
+                xmlns:mx="library://ns.adobe.com/flex/mx" click="itemrenderer1_clickHandler(event)">
+
+     <fx:Script>
+        <![CDATA[
+            import spark.components.supportClasses.GroupBase;
+            import spark.effects.Animate;
+            import spark.effects.animation.MotionPath;
+            import spark.effects.animation.SimpleMotionPath;
+            protected function itemrenderer1_clickHandler(event:MouseEvent):void
+            {
+                var g:GroupBase = parent as GroupBase;
+                var p:Point = g.layout.getScrollPositionDeltaToElement(this.itemIndex);
+                if (p)
+                {
+                    var startX:Number = g.horizontalScrollPosition;
+                    var startY:Number = g.verticalScrollPosition;
+                    var anim:Animate = new Animate();
+                    anim.motionPaths = new <MotionPath>[
+                        new SimpleMotionPath("horizontalScrollPosition", startX, startX + p.x, 500),
+                        new SimpleMotionPath("verticalScrollPosition", startY, startY + p.y, 500)
+                    ];
+                    
+                    var interpolator:NumberInterpolatorWrapping = new NumberInterpolatorWrapping(0, g.contentWidth - g.width);
+                    var scrollLength:Number = interpolator.getLength(startX, startX + p.x);
+                    anim.interpolator = interpolator;
+                    anim.duration = Math.max(550, Math.min(2500, scrollLength * 2));
+                    
+                    anim.play([g]);
+                }
+            }
+        ]]>
+    </fx:Script>
+
+	
+	<s:states>
+	    <s:State name="normal"/>
+        <s:State name="hovered"/>
+        <s:State name="selected"/>
+	</s:states>
+	
+    <s:Rect id="border" left="0" right="0" top="0" bottom="0">
+        <s:fill>
+            <s:SolidColor color="0xDFDFDF" color.hovered="0xFF0000" color.selected="0x00FF00"/>
+        </s:fill>
+    </s:Rect>
+
+	<s:Group left="1" right="1" top="1" bottom="1">
+    	<s:layout>
+    	    <s:VerticalLayout horizontalAlign="center"/>
+    	</s:layout>
+    	
+    	<mx:Image 
+    		width="75" height="75"
+    		source="{data.thumbnail.url}" />
+    	<s:Label text="{data.credit}" maxWidth="100" textAlign="center"/>
+    </s:Group>
+	
+</s:ItemRenderer>
+

http://git-wip-us.apache.org/repos/asf/flex-examples/blob/9343d391/tourdeflexmodules/src/spark/layouts/FlowLayout1.as
----------------------------------------------------------------------
diff --git a/tourdeflexmodules/src/spark/layouts/FlowLayout1.as b/tourdeflexmodules/src/spark/layouts/FlowLayout1.as
new file mode 100644
index 0000000..9462285
--- /dev/null
+++ b/tourdeflexmodules/src/spark/layouts/FlowLayout1.as
@@ -0,0 +1,195 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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
+{
+import mx.core.ILayoutElement;
+
+import spark.components.supportClasses.GroupBase;
+import spark.layouts.supportClasses.LayoutBase;
+
+public class FlowLayout1 extends LayoutBase
+{
+    
+    //---------------------------------------------------------------
+    //
+    //  Class properties
+    //
+    //---------------------------------------------------------------
+    
+    //---------------------------------------------------------------
+    //  horizontalGap
+    //---------------------------------------------------------------
+
+    private var _horizontalGap:Number = 10;
+
+    public function set horizontalGap(value:Number):void
+    {
+        _horizontalGap = value;
+        
+        // We must invalidate the layout
+        var layoutTarget:GroupBase = target;
+        if (layoutTarget)
+            layoutTarget.invalidateDisplayList();
+    }
+    
+    //---------------------------------------------------------------
+    //  verticalAlign
+    //---------------------------------------------------------------
+
+    private var _verticalAlign:String = "bottom";
+    
+    public function set verticalAlign(value:String):void
+    {
+        _verticalAlign = value;
+        
+        // We must invalidate the layout
+        var layoutTarget:GroupBase = target;
+        if (layoutTarget)
+            layoutTarget.invalidateDisplayList();
+    }
+    
+	//---------------------------------------------------------------
+	//  horizontalAlign
+	//---------------------------------------------------------------
+	
+	private var _horizontalAlign:String = "left"; // center, right
+	
+	public function set horizontalAlign(value:String):void
+	{
+		_horizontalAlign = value;
+		
+		// We must invalidate the layout
+		var layoutTarget:GroupBase = target;
+		if (layoutTarget)
+			layoutTarget.invalidateDisplayList();
+	}
+	
+    //---------------------------------------------------------------
+    //
+    //  Class methods
+    //
+    //---------------------------------------------------------------
+    
+    override public function updateDisplayList(containerWidth:Number,
+                                               containerHeight:Number):void
+    {
+        var element:ILayoutElement;
+        var layoutTarget:GroupBase = target;
+        var count:int = layoutTarget.numElements;
+        var hGap:Number = _horizontalGap;
+
+        // The position for the current element
+        var x:Number = 0;
+        var y:Number = 0;
+        var elementWidth:Number;
+        var elementHeight:Number;
+
+        var vAlign:Number = 0;
+        switch (_verticalAlign)
+        {
+            case "middle" : vAlign = 0.5; break;
+            case "bottom" : vAlign = 1; break;
+        }
+
+        // Keep track of per-row height, maximum row width
+        var maxRowWidth:Number = 0;
+
+        // loop through the elements
+        // while we can start a new row
+        var rowStart:int = 0;
+        while (rowStart < count)
+        {
+            // The row always contains the start element
+            element = useVirtualLayout ? layoutTarget.getVirtualElementAt(rowStart) :
+										 layoutTarget.getElementAt(rowStart);
+									     
+            var rowWidth:Number = element.getPreferredBoundsWidth();
+            var rowHeight:Number =  element.getPreferredBoundsHeight();
+            
+            // Find the end of the current row
+            var rowEnd:int = rowStart;
+            while (rowEnd + 1 < count)
+            {
+                element = useVirtualLayout ? layoutTarget.getVirtualElementAt(rowEnd + 1) :
+										     layoutTarget.getElementAt(rowEnd + 1);
+                
+                // Since we haven't resized the element just yet, get its preferred size
+                elementWidth = element.getPreferredBoundsWidth();
+                elementHeight = element.getPreferredBoundsHeight();
+
+                // Can we add one more element to this row?
+                if (rowWidth + hGap + elementWidth > containerWidth)
+                    break;
+
+                rowWidth += hGap + elementWidth;
+                rowHeight = Math.max(rowHeight, elementHeight);
+                rowEnd++;
+            }
+            
+			x = 0;
+			switch (_horizontalAlign)
+			{
+				case "center" : x = Math.round(containerWidth - rowWidth) / 2; break;
+				case "right" : x = containerWidth - rowWidth;
+			}
+			
+            // Keep track of the maximum row width so that we can
+            // set the correct contentSize
+            maxRowWidth = Math.max(maxRowWidth, x + rowWidth);
+
+            // Layout all the elements within the row
+            for (var i:int = rowStart; i <= rowEnd; i++) 
+            {
+                element = useVirtualLayout ? layoutTarget.getVirtualElementAt(i) : 
+											 layoutTarget.getElementAt(i);
+
+                // Resize the element to its preferred size by passing
+                // NaN for the width and height constraints
+                element.setLayoutBoundsSize(NaN, NaN);
+
+                // Find out the element's dimensions sizes.
+                // We do this after the element has been already resized
+                // to its preferred size.
+                elementWidth = element.getLayoutBoundsWidth();
+                elementHeight = element.getLayoutBoundsHeight();
+
+                // Calculate the position within the row
+                var elementY:Number = Math.round((rowHeight - elementHeight) * vAlign);
+
+                // Position the element
+                element.setLayoutBoundsPosition(x, y + elementY);
+
+                x += hGap + elementWidth;
+            }
+
+            // Next row will start with the first element after the current row's end
+            rowStart = rowEnd + 1;
+
+            // Update the position to the beginning of the row
+            x = 0;
+            y += rowHeight;
+        }
+
+        // Set the content size which determines the scrolling limits
+        // and is used by the Scroller to calculate whether to show up
+        // the scrollbars when the the scroll policy is set to "auto"
+        layoutTarget.setContentSize(maxRowWidth, y);
+    }
+}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/flex-examples/blob/9343d391/tourdeflexmodules/src/spark/layouts/HBaselineLayout.as
----------------------------------------------------------------------
diff --git a/tourdeflexmodules/src/spark/layouts/HBaselineLayout.as b/tourdeflexmodules/src/spark/layouts/HBaselineLayout.as
new file mode 100644
index 0000000..5d87ce7
--- /dev/null
+++ b/tourdeflexmodules/src/spark/layouts/HBaselineLayout.as
@@ -0,0 +1,199 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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 {
+
+import mx.core.ILayoutElement;
+import mx.events.PropertyChangeEvent;
+import mx.formatters.NumberBase;
+
+import spark.components.supportClasses.GroupBase;
+import spark.layouts.HorizontalLayout;
+
+public class HBaselineLayout extends HorizontalLayout
+{
+	public function HBaselineLayout()
+	{
+		super();
+	}
+
+	//----------------------------------
+	//  globalBaseline
+	//----------------------------------
+	
+	[Inspectable(category="General")]
+
+	private var _globalBaseline:Number = NaN;
+	public function get globalBaseline():Number
+	{
+		return _globalBaseline;
+	}
+
+	public function set globalBaseline(value:Number):void
+	{
+		_globalBaseline = value;
+		var target:GroupBase = this.target;
+		if (target)
+		{
+			target.invalidateSize();
+			target.invalidateDisplayList();
+		}
+	}
+
+	//----------------------------------
+	//  actualBaseline
+	//----------------------------------
+	
+	private var _actualBaseline:Number;
+	
+	[Bindable("propertyChange")]
+	[Inspectable(category="General")]
+	
+	public function get actualBaseline():Number
+	{
+		return _actualBaseline;
+	}
+	
+	private function setActualBaseline(value:Number):void
+	{
+		if (value == _actualBaseline)
+			return;
+
+		var oldValue:Number = _actualBaseline;
+		_actualBaseline = value;
+		dispatchEvent(PropertyChangeEvent.createUpdateEvent(this, "actualBaseline", oldValue, value));
+	}
+	
+	//----------------------------------
+	//  verticalAlign
+	//----------------------------------
+	
+	[Inspectable(category="General", enumeration="top,bottom,middle,justify,contentJustify,baseline", defaultValue="top")]
+	override public function get verticalAlign():String
+	{
+		return super.verticalAlign;		
+	}
+
+	/**
+	 *  @private 
+	 */
+	override public function measure():void
+	{
+		super.measure();
+		
+		var target:GroupBase = this.target;
+		if (!target || verticalAlign != "baseline")
+			return;
+		
+		measureBaseline(true /*usePreferredSize*/);
+		if (!isNaN(_globalBaseline))
+			measuredBaselineTop = _globalBaseline;
+		
+		// The measured height is the sum of the space above and below the baseline
+		if (isNaN(paddingTop))
+			measuredBaselineTop += paddingTop;
+		if (isNaN(paddingBottom))
+			measuredBaselineBottom += paddingBottom;
+		target.measuredHeight = Math.round(measuredBaselineTop + measuredBaselineBottom);
+	}
+	
+	/**
+	 *  @private 
+	 */
+	override public function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
+	{
+		super.updateDisplayList(unscaledWidth, unscaledHeight);
+		
+		var target:GroupBase = this.target;
+		if (!target || verticalAlign != "baseline")
+			return;
+
+		measureBaseline(false /*usePreferredSize*/);
+		if (!isNaN(_globalBaseline))
+			measuredBaselineTop = _globalBaseline;
+
+		if (isNaN(paddingTop))
+			measuredBaselineTop += paddingTop;
+		
+		// Adjust the position of the elements
+		var contentHeight:Number = 0;
+		var count:int = target.numElements;
+		for (var i:int = 0; i < count; i++)
+		{
+			var element:ILayoutElement = target.getElementAt(i);
+			if (!element || !element.includeInLayout)
+				continue;
+			
+			var elementBaseline:Number = element.baseline as Number;
+			if (isNaN(elementBaseline))
+				elementBaseline = 0;
+
+			var baselinePosition:Number = element.baselinePosition;
+			var y:Number = measuredBaselineTop + (elementBaseline - baselinePosition);
+			element.setLayoutBoundsPosition(element.getLayoutBoundsX(), y);
+			contentHeight = Math.max(contentHeight, element.getLayoutBoundsHeight() + y);
+		}
+
+		// Adjust the content height
+		if (isNaN(paddingBottom))
+			contentHeight += paddingBottom;
+		target.setContentSize(target.contentWidth, contentHeight);
+		
+		// Update the baseline
+		setActualBaseline(measuredBaselineTop);
+	}
+
+	private var measuredBaselineTop:Number = 0;			// How much space is needed above the baseline to fit all the elements
+	private var measuredBaselineBottom:Number = 0;		// How much space is needed below the baseline to fit all the elements
+	
+	/**
+	 *  @private 
+	 */
+	private function measureBaseline(usePreferredSize:Boolean):void
+	{
+		var elementBaseline:Number = 0; 			// The current element's explicit baseline constraint
+		var elementBaselineTop:Number = 0;			// The portiono of the current element that's above the baseline
+		var elementBaselineBottom:Number = 0;		// The portion of the current element that's below the baseline
+
+		measuredBaselineTop = 0;
+		measuredBaselineBottom = 0;
+
+		var count:int = target.numElements;
+		for (var i:int = 0; i < count; i++)
+		{
+			var element:ILayoutElement = target.getElementAt(i);
+			if (!element || !element.includeInLayout)
+				continue;
+			
+			var elementHeight:Number = usePreferredSize ? element.getPreferredBoundsHeight() :
+														  element.getLayoutBoundsHeight();
+			elementBaseline = element.baseline as Number;
+			if (isNaN(elementBaseline))
+				elementBaseline = 0;
+			
+			var baselinePosition:Number = element.baselinePosition;
+			
+			elementBaselineTop = baselinePosition - elementBaseline;
+			elementBaselineBottom = elementHeight - elementBaselineTop;
+			
+			measuredBaselineTop = Math.max(elementBaselineTop, measuredBaselineTop);
+			measuredBaselineBottom = Math.max(elementBaselineBottom, measuredBaselineBottom);
+		}
+	}
+}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/flex-examples/blob/9343d391/tourdeflexmodules/src/spark/layouts/NumberInterpolatorWrapping.as
----------------------------------------------------------------------
diff --git a/tourdeflexmodules/src/spark/layouts/NumberInterpolatorWrapping.as b/tourdeflexmodules/src/spark/layouts/NumberInterpolatorWrapping.as
new file mode 100644
index 0000000..8613d7b
--- /dev/null
+++ b/tourdeflexmodules/src/spark/layouts/NumberInterpolatorWrapping.as
@@ -0,0 +1,110 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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
+{
+import spark.effects.interpolation.IInterpolator;
+    
+public class NumberInterpolatorWrapping implements IInterpolator
+{
+    private var _rangeBegin:Number;
+    private var _rangeEnd:Number;
+    public function NumberInterpolatorWrapping(rangeBegin:Number, rangeEnd:Number)
+    {
+        _rangeBegin = rangeBegin;
+        _rangeEnd = rangeEnd;
+    }
+        
+    /**
+     * Returns the type that an implementor can handle
+     */
+    public function get interpolatedType():Class
+    {
+        return Number;
+    }
+    
+    /**
+     * Given an elapsed fraction of an animation between 0 and 1,
+     * and start and end values, this function returns some value
+     * based on whatever interpolation the implementor chooses to
+     * provide.
+     */
+    public function interpolate(fraction:Number, startValue:Object, endValue:Object):Object
+    {
+        if (fraction == 0)
+            return startValue;
+        else if (fraction == 1)
+            return endValue;
+        var start:Number = Number(startValue);
+        var end:Number = Number(endValue);
+
+        if (Math.abs(end - start) < Math.abs(_rangeEnd - _rangeBegin) - Math.abs(end - start))
+        {
+            return start + fraction * (end - start);
+        }
+        else
+        {
+            var result:Number;
+            if (start < end)
+            {
+                result = start - fraction * (_rangeEnd - _rangeBegin - Math.abs(start - end));
+                if (result < _rangeBegin)
+                    result += _rangeEnd - _rangeBegin;
+                return result;
+            }
+            else
+            {
+                result = start + fraction * (_rangeEnd - _rangeBegin - Math.abs(start - end));
+                if (result > _rangeEnd)
+                    result -= _rangeEnd - _rangeBegin;
+                return result;
+            }
+        }        
+    }
+    
+    public function increment(baseValue:Object, incrementValue:Object):Object
+    {
+        var result:Number = Number(baseValue) + Number(incrementValue);
+        // This won't handle situations where we're adding more than
+        // the range itself, but since this will only be called when
+        // the user submits a 'byValue' that large, it seems unlikely
+        // at the very least
+        if (result > _rangeEnd)
+            result = _rangeBegin + (result - _rangeEnd);
+        return result;
+    }
+    
+    public function decrement(baseValue:Object, decrementValue:Object):Object
+    {
+        var result:Number = Number(baseValue) - Number(decrementValue);
+        // This won't handle situations where we're subtracting more than
+        // the range itself, but since this will only be called when
+        // the user submits a 'byValue' that large, it seems unlikely
+        // at the very least
+        if (result < _rangeBegin)
+            result = _rangeEnd + (_rangeBegin - result);
+        return result;
+    }
+    
+    public function getLength(startValue:Number, endValue:Number):Number
+    {
+        return Math.min( Math.abs(startValue - endValue), Math.abs(_rangeEnd - _rangeBegin - Math.abs(startValue - endValue)));
+    }   
+
+}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/flex-examples/blob/9343d391/tourdeflexmodules/src/spark/layouts/WheelLayout.as
----------------------------------------------------------------------
diff --git a/tourdeflexmodules/src/spark/layouts/WheelLayout.as b/tourdeflexmodules/src/spark/layouts/WheelLayout.as
new file mode 100644
index 0000000..1ce4bb6
--- /dev/null
+++ b/tourdeflexmodules/src/spark/layouts/WheelLayout.as
@@ -0,0 +1,516 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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
+{
+
+import flash.geom.Matrix;
+import flash.geom.Matrix3D;
+import flash.geom.Point;
+import flash.geom.Rectangle;
+import flash.geom.Vector3D;
+
+import mx.core.ILayoutElement;
+import mx.core.IVisualElement;
+
+import spark.components.supportClasses.GroupBase;
+import spark.core.NavigationUnit;
+import spark.layouts.supportClasses.LayoutBase;
+
+public class WheelLayout extends LayoutBase
+{
+    //--------------------------------------------------------------------------
+    //
+    //  Constructor
+    //
+    //--------------------------------------------------------------------------
+
+    public function WheelLayout()
+    {
+        super();
+    }
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Properties
+    //
+    //--------------------------------------------------------------------------
+
+    //----------------------------------
+    //  gap
+    //----------------------------------
+
+    private var _gap:Number = 0;
+
+    /**
+     *  The gap between the items
+     */
+    public function get gap():Number
+    {
+        return _gap;
+    }
+    
+    public function set gap(value:Number):void
+    {
+        _gap = value;
+        var layoutTarget:GroupBase = target;
+        if (layoutTarget)
+        {
+            layoutTarget.invalidateSize();
+            layoutTarget.invalidateDisplayList();
+        }
+    }
+    
+    //----------------------------------
+    //  axisAngle
+    //----------------------------------
+
+    /**
+     *  @private  
+     *  The total width of all items, including gap space.
+     */
+    private var _totalWidth:Number;
+
+    /**
+     *  @private  
+     *  Cache which item is currently in view, to facilitate scrollposition delta calculations
+     */
+    private var _centeredItemIndex:int = 0;
+    private var _centeredItemCircumferenceBegin:Number = 0;
+    private var _centeredItemCircumferenceEnd:Number = 0;
+    private var _centeredItemDegrees:Number = 0;
+
+    /**
+     *  The axis to tilt the 3D wheel 
+     */
+    private var _axis:Vector3D = new Vector3D(0, 1, 0.1);
+    
+    /**
+     *  The angle to tilt the axis of the wheel
+     */
+    public function set axisAngle(value:Number):void
+    {
+        _axis = new Vector3D(0, Math.cos(Math.PI * value /180), Math.sin(Math.PI * value /180));
+        var layoutTarget:GroupBase = target;
+        if (layoutTarget)
+        {
+            layoutTarget.invalidateSize();
+            layoutTarget.invalidateDisplayList();
+        }
+    }
+    
+    /**
+     *  @private 
+     *  Given the radius of the sphere, return the radius of the
+     *  projected sphere. Uses the projection matrix of the
+     *  layout target to calculate.
+     */    
+    private function projectSphere(radius:Number, radius1:Number):Number
+    {
+        var fl:Number = target.transform.perspectiveProjection.focalLength;
+        var alpha:Number = Math.asin( radius1 / (radius + fl) );
+        return fl * Math.tan(alpha) * 2;
+    }
+    
+    /**
+     *  @private
+     *  Given the totalWidth, maxHeight and maxHalfWidthDiagonal, calculate the bounds of the items
+     *  on screen.  Uses the projection matrix of the layout target to calculate. 
+     */
+    private function projectBounds(totalWidth:Number, maxWidth:Number, maxHeight:Number, maxHalfWidthDiagonal:Number):Point
+    {
+        // Use the the total width as a circumference of an imaginary circle which we will use to
+        // align the items in 3D:
+        var radius:Number = _totalWidth * 0.5 / Math.PI;
+        
+        // Now since we are going to arrange all the items along circle, middle of the item being the tangent point,
+        // we need to calculate the minimum bounding circle. It is easily calculated from the maximum width item:
+        var boundingRadius:Number = Math.sqrt(radius * radius + 0.25 * maxWidth * maxWidth);
+                                      
+        var projectedBoundsW:Number = _axis.z * _axis.z * (maxHalfWidthDiagonal + 2 * radius) + 
+                                      projectSphere(radius, boundingRadius ) * _axis.y * _axis.y;
+                                      
+        var projectedBoundsH:Number = Math.abs(_axis.z) * (maxHalfWidthDiagonal + 2 * radius) +
+                                      maxHeight * _axis.y * _axis.y;
+                                      
+        return new Point(projectedBoundsW + 10, projectedBoundsH + 10);
+    }
+
+    /**
+     *  @private 
+     *  Iterates through all the items, calculates the projected bounds on screen, updates _totalWidth member variable.
+     */    
+    private function calculateBounds():Point
+    {
+        // Calculate total width:
+        _totalWidth = 0;
+     
+        var maxHeight:Number = 0;
+        var maxWidth:Number = 0;
+        var maxD:Number = 0;
+   
+        // Add up all the widths
+        var iter:LayoutIterator = new LayoutIterator(target);
+        var el:ILayoutElement;
+        while (el = iter.nextElement())
+        {
+            var preferredWidth:Number = el.getPreferredBoundsWidth(false /*postTransform*/);
+            var preferredHeight:Number = el.getPreferredBoundsHeight(false /*postTransform*/);
+
+            // Add up item width
+            _totalWidth += preferredWidth;
+            
+            // Max up item size
+            maxWidth = Math.max(maxWidth, preferredWidth);
+            maxHeight = Math.max(maxHeight, preferredHeight);
+            
+            maxD = Math.max(maxD, Math.sqrt(preferredWidth * preferredWidth / 4 + 
+                                            preferredHeight * preferredHeight));    
+        }
+        
+        // Add up the gap
+        _totalWidth += gap * iter.numVisited;
+
+        // Project        
+        return projectBounds(_totalWidth, maxWidth, maxHeight, maxD);
+    }
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Overridden methods: LayoutBase
+    //
+    //--------------------------------------------------------------------------
+    
+    /**
+     * @private
+     */
+    override public function set target(value:GroupBase):void
+    {
+        // Make sure that if layout is swapped out, we clean up
+        if (!value && target)
+        {
+            target.maintainProjectionCenter = false;
+
+            var iter:LayoutIterator = new LayoutIterator(target);
+            var el:ILayoutElement;
+            while (el = iter.nextElement())
+            {
+                el.setLayoutMatrix(new Matrix(), false /*triggerLayout*/);
+            }
+        }
+        
+        super.target = value;
+
+        // Make sure we turn on projection the first time the layout
+        // gets assigned to the group
+        if (target)
+            target.maintainProjectionCenter = true;
+    }
+    
+    override public function measure():void
+    {
+        var bounds:Point = calculateBounds();
+        
+        target.measuredWidth = bounds.x;
+        target.measuredHeight = bounds.y;
+    }
+    
+    override public function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
+    {
+        // Get the bounds, this will also update _totalWidth
+        var bounds:Point = calculateBounds();
+
+        // Update the content size
+        target.setContentSize(_totalWidth + unscaledWidth, bounds.y); 
+        var radius:Number = _totalWidth * 0.5 / Math.PI;
+        var gap:Number = this.gap;
+        _centeredItemDegrees = Number.MAX_VALUE;
+        
+        var scrollPosition:Number = target.horizontalScrollPosition;
+        var totalWidthSoFar:Number = 0;
+        // Subtract the half width of the first element from totalWidthSoFar: 
+        var iter:LayoutIterator = new LayoutIterator(target);
+        var el:ILayoutElement = iter.nextElement();
+        if (!el)
+            return;
+        totalWidthSoFar -= el.getPreferredBoundsWidth(false /*postTransform*/) / 2;
+        
+        // Set the 3D Matrix for all the elements:
+        iter.reset();
+        while (el = iter.nextElement())
+        { 
+            // Size the item, no need to position it, since we'd set the computed matrix
+            // which defines the position.
+            el.setLayoutBoundsSize(NaN, NaN, false /*postTransform*/);
+            var elementWidth:Number = el.getLayoutBoundsWidth(false /*postTransform*/);
+            var elementHeight:Number = el.getLayoutBoundsHeight(false /*postTransform*/); 
+            var degrees:Number = 360 * (totalWidthSoFar + elementWidth/2 - scrollPosition) / _totalWidth; 
+
+            // Remember which item is centered, this is used during scrolling
+            var curDegrees:Number = degrees % 360;
+            if (Math.abs(curDegrees) < Math.abs(_centeredItemDegrees))
+            {
+                _centeredItemDegrees = curDegrees;
+                _centeredItemIndex = iter.curIndex;
+                _centeredItemCircumferenceBegin = totalWidthSoFar - gap;
+                _centeredItemCircumferenceEnd = totalWidthSoFar + elementWidth + gap;
+            }
+
+            // Calculate and set the 3D Matrix 
+            var m:Matrix3D = new Matrix3D();
+            m.appendTranslation(-elementWidth/2, -elementHeight/2 + radius * _axis.z, -radius * _axis.y );
+            m.appendRotation(-degrees, _axis);
+            m.appendTranslation(unscaledWidth/2, unscaledHeight/2, radius * _axis.y);
+            el.setLayoutMatrix3D(m, false /*triggerLayout*/);
+            
+            // Update the layer for a correct z-order
+            if (el is IVisualElement)
+                IVisualElement(el).depth = Math.abs( Math.floor(180 - Math.abs(degrees % 360)) );
+
+            // Move on to next item
+            totalWidthSoFar += elementWidth + gap;
+        }
+    }
+    
+    private function scrollPositionFromCenterToNext(next:Boolean):Number
+    {
+        var iter:LayoutIterator = new LayoutIterator(target, _centeredItemIndex);
+        var el:ILayoutElement = next ? iter.nextElementWrapped() : iter.prevElementWrapped();
+        if (!el)
+            return 0;
+        
+        var elementWidth:Number = el.getLayoutBoundsWidth(false /*postTransform*/);
+        
+        var value:Number; 
+        if (next)
+        {
+            if (_centeredItemDegrees > 0.1)
+                return (_centeredItemCircumferenceEnd + _centeredItemCircumferenceBegin) / 2;
+            
+            value = _centeredItemCircumferenceEnd + elementWidth/2;
+            if (value > _totalWidth)
+                value -= _totalWidth;
+        }
+        else
+        {
+            if (_centeredItemDegrees < -0.1)
+                return (_centeredItemCircumferenceEnd + _centeredItemCircumferenceBegin) / 2;
+
+            value = _centeredItemCircumferenceBegin - elementWidth/2;
+            if (value < 0)
+                value += _totalWidth;
+        }
+        return value;     
+    }
+    
+    override protected function scrollPositionChanged():void
+    {
+        if (target)
+            target.invalidateDisplayList();
+    }
+
+    override public function getHorizontalScrollPositionDelta(navigationUnit:uint):Number
+    {
+        var g:GroupBase = target;
+        if (!g || g.numElements == 0)
+            return 0;
+            
+        var value:Number;     
+
+        switch (navigationUnit)
+        {
+            case NavigationUnit.LEFT:
+            {
+                value = target.horizontalScrollPosition - 30;
+                if (value < 0)
+                    value += _totalWidth;
+                return value - target.horizontalScrollPosition;
+            }
+                
+            case NavigationUnit.RIGHT:
+            {
+                value = target.horizontalScrollPosition + 30;
+                if (value > _totalWidth)
+                    value -= _totalWidth;
+                return value - target.horizontalScrollPosition;
+            }
+                
+            case NavigationUnit.PAGE_LEFT:
+                return scrollPositionFromCenterToNext(false) - target.horizontalScrollPosition;
+                
+            case NavigationUnit.PAGE_RIGHT:
+                return scrollPositionFromCenterToNext(true) - target.horizontalScrollPosition;
+                
+            case NavigationUnit.HOME: 
+                return 0;
+                
+            case NavigationUnit.END: 
+                return _totalWidth;
+                
+            default:
+                return 0;
+        }       
+    }
+    
+    /**
+     *  @private
+     */ 
+    override public function getScrollPositionDeltaToElement(index:int):Point
+    {
+        var layoutTarget:GroupBase = target;
+        if (!layoutTarget)
+            return null;
+       
+        var gap:Number = this.gap;     
+        var totalWidthSoFar:Number = 0;
+        var iter:LayoutIterator = new LayoutIterator(layoutTarget);
+
+        var el:ILayoutElement = iter.nextElement();
+        if (!el)
+            return null;
+        totalWidthSoFar -= el.getLayoutBoundsWidth(false /*postTransform*/) / 2;
+
+        iter.reset();
+        while (null != (el = iter.nextElement()) && iter.curIndex <= index)
+        {    
+            var elementWidth:Number = el.getLayoutBoundsWidth(false /*postTransform*/);
+            totalWidthSoFar += gap + elementWidth;
+        }
+        return new Point(totalWidthSoFar - elementWidth / 2 -gap - layoutTarget.horizontalScrollPosition, 0);
+    }
+
+    /**
+     *  @private
+     */ 
+    override public function updateScrollRect(w:Number, h:Number):void
+    {
+        var g:GroupBase = target;
+        if (!g)
+            return;
+            
+        if (clipAndEnableScrolling)
+        {
+            // Since scroll position is reflected in our 3D calculations,
+            // always set the top-left of the srcollRect to (0,0).
+            g.scrollRect = new Rectangle(0, verticalScrollPosition, w, h);
+        }
+        else
+            g.scrollRect = null;
+    } 
+}
+}
+
+import mx.core.ILayoutElement;
+
+import spark.components.supportClasses.GroupBase;
+    
+class LayoutIterator 
+{
+    private var _curIndex:int;
+    private var _numVisited:int = 0;
+    private var totalElements:int;
+    private var _target:GroupBase;
+    private var _loopIndex:int = -1;
+	private var _useVirtual:Boolean;
+
+    public function get curIndex():int
+    {
+        return _curIndex;
+    }
+
+    public function LayoutIterator(target:GroupBase, index:int=-1):void
+    {
+        totalElements = target.numElements;
+        _target = target;
+        _curIndex = index;
+		_useVirtual = _target.layout.useVirtualLayout;
+    }
+
+    public function nextElement():ILayoutElement
+    {
+        while (_curIndex < totalElements - 1)
+        {
+            var el:ILayoutElement = _useVirtual ? _target.getVirtualElementAt(++_curIndex) :
+										 		  _target.getElementAt(++_curIndex);
+            if (el && el.includeInLayout)
+            {
+                ++_numVisited;
+                return el;
+            }
+        }
+        return null;
+    }
+    
+    public function prevElement():ILayoutElement
+    {
+        while (_curIndex > 0)
+        {
+            var el:ILayoutElement = _useVirtual ? _target.getVirtualElementAt(--_curIndex) :
+												  _target.getElementAt(--_curIndex);
+            if (el && el.includeInLayout)
+            {
+                ++_numVisited;
+                return el;
+            }
+        }
+        return null;
+    }
+
+    public function nextElementWrapped():ILayoutElement
+    {
+        if (_loopIndex == -1)
+            _loopIndex = _curIndex;
+        else if (_loopIndex == _curIndex)
+            return null;
+
+        var el:ILayoutElement = nextElement();
+        if (el)
+            return el;
+        else if (_curIndex == totalElements - 1)
+            _curIndex = -1;
+        return nextElement();
+    }
+    
+    public function prevElementWrapped():ILayoutElement
+    {
+        if (_loopIndex == -1)
+            _loopIndex = _curIndex;
+        else if (_loopIndex == _curIndex)
+            return null;
+
+        var el:ILayoutElement = prevElement();
+        if (el)
+            return el;
+        else if (_curIndex == 0)
+            _curIndex = totalElements;
+        return prevElement();
+    }
+
+    public function reset():void
+    {
+        _curIndex = -1;
+        _numVisited = 0;
+        _loopIndex = -1;
+    }
+
+    public function get numVisited():int
+    {
+        return _numVisited;
+    }
+}
+    

http://git-wip-us.apache.org/repos/asf/flex-examples/blob/9343d391/tourdeflexmodules/src/spark/layouts/assets/ApacheFlexIcon.png
----------------------------------------------------------------------
diff --git a/tourdeflexmodules/src/spark/layouts/assets/ApacheFlexIcon.png b/tourdeflexmodules/src/spark/layouts/assets/ApacheFlexIcon.png
new file mode 100644
index 0000000..e68d831
Binary files /dev/null and b/tourdeflexmodules/src/spark/layouts/assets/ApacheFlexIcon.png differ

http://git-wip-us.apache.org/repos/asf/flex-examples/blob/9343d391/tourdeflexmodules/src/spark/layouts/assets/xdslider.png
----------------------------------------------------------------------
diff --git a/tourdeflexmodules/src/spark/layouts/assets/xdslider.png b/tourdeflexmodules/src/spark/layouts/assets/xdslider.png
new file mode 100644
index 0000000..8d49a7a
Binary files /dev/null and b/tourdeflexmodules/src/spark/layouts/assets/xdslider.png differ

http://git-wip-us.apache.org/repos/asf/flex-examples/blob/9343d391/tourdeflexmodules/src/spark/layouts/data/catalog.xml
----------------------------------------------------------------------
diff --git a/tourdeflexmodules/src/spark/layouts/data/catalog.xml b/tourdeflexmodules/src/spark/layouts/data/catalog.xml
new file mode 100644
index 0000000..7fbf54e
--- /dev/null
+++ b/tourdeflexmodules/src/spark/layouts/data/catalog.xml
@@ -0,0 +1,179 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+  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.
+
+-->
+
+<catalog>
+
+    <product productId="1">
+        <name>Phone A</name>
+        <color></color>
+        <description>Easy to use without sacrificing style, this phone offers functional voice communication supported by text messaging, multimedia messaging, mobile internet, games and more.</description>
+        <price>99.99</price>
+        <series>6000</series>
+        <triband>false</triband>
+        <camera>false</camera>
+        <video>false</video>
+        <highlight1>MMS</highlight1>
+		<highlight2>Large color display</highlight2>
+    </product>
+
+    <product productId="2">
+        <name>Phone B</name>
+        <color>Blue</color>
+        <description>Light up the night with a glow-in-the-dark cover - when it&#39;s charged with light you can easily find your phone in the dark. When you get a call, this phone flashes in tune with your ringing tone. And when you snap on a gaming cover, you&#39;ll get luminescent light effects in time to the gaming action.</description>
+        <price>139</price>
+        <series>3000</series>
+        <triband>true</triband>
+        <camera>false</camera>
+        <video>false</video>
+        <highlight1>Glow-in-the-dark</highlight1>
+		<highlight2>Flashing lights</highlight2>
+    </product>
+
+    <product productId="3">
+        <name>Phone C</name>
+        <color>Pink</color>
+        <description>Light up the night with a glow-in-the-dark cover - when it&#39;s charged with light you can easily find your phone in the dark. When you get a call, this phone flashes in tune with your ringing tone. And when you snap on a gaming cover, you&#39;ll get luminescent light effects in time to the gaming action.</description>
+        <price>139</price>
+        <series>3000</series>
+        <triband>true</triband>
+        <camera>false</camera>
+        <video>false</video>
+        <highlight1>Glow-in-the-dark</highlight1>
+		<highlight2>Flashing lights</highlight2>
+    </product>
+
+    <product productId="4">
+        <name>Phone D</name>
+        <color></color>
+        <description>Designed for both business and pleasure, this phone offers a pleasing mix of features. Enclosed within its chic, compact body, you will discover the benefits of tri-band compatibility, a color screen, MMS, XHTML browsing, cheerful screensavers, and much more.</description>
+        <price>159.99</price>
+        <series>3000</series>
+        <triband>true</triband>
+        <camera>false</camera>
+        <video>false</video>
+        <highlight1>Multimedia messaging</highlight1>
+		<highlight2>Animated screensavers</highlight2>
+    </product>
+
+    <product productId="5">
+        <name>Phone E</name>
+        <color></color>
+        <description>This phone is a fresh new cut on some familiar ideas - animate your MMS messages with cute characters, see the music with lights that flash in time with your ringing tone, download wallpapers and screensavers with matching color schemes for the interface.</description>
+        <price>159.99</price>
+        <series>3000</series>
+        <triband>false</triband>
+        <camera>true</camera>
+        <video>false</video>
+        <highlight1>MIDI tones</highlight1>
+		<highlight2>Cut-out covers</highlight2>
+    </product>
+
+    <product productId="6">
+        <name>Phone F</name>
+        <color></color>
+        <description>Messaging is more personal, versatile and fun with this camera phone.  Capture experiences as soon as you see them and send the photos you take to you friends and family.</description>
+        <price>199.99</price>
+        <series>3000</series>
+        <triband>false</triband>
+        <camera>true</camera>
+        <video>true</video>
+        <highlight1>Infrared or Bluetooth</highlight1>
+		<highlight2>Built-in XHTML browser</highlight2>
+    </product>
+
+    <product productId="7">
+        <name>Phone G</name>
+        <color></color>
+        <description>Messaging just got a whole lot smarter. This messaging device puts the tools you need for rich communication - full messaging keyboard, digital camera, mobile email, MMS, SMS, and Instant Messaging - right at your fingertips, in a small, sleek device.</description>
+        <price>299.99</price>
+        <series>6000</series>
+        <triband>true</triband>
+        <camera>true</camera>
+        <video>false</video>
+        <highlight1>Messaging keyboard</highlight1>
+		<highlight2>Bluetooth</highlight2>
+    </product>
+
+    <product productId="8">
+        <name>Phone F</name>
+        <color></color>
+        <description>Classic business tools meet your creative streak in this imaging smartphone. It has a Web browser with PDF support, document viewer applications for email attachments, a direct printing application, and a megapixel still camera that also shoots up to 10 minutes of video.</description>
+        <price>309.99</price>
+        <series>6000</series>
+        <triband>false</triband>
+        <camera>true</camera>
+        <video>true</video>
+        <highlight1>PDF support</highlight1>
+		<highlight2>4x digital zoom</highlight2>
+    </product>
+
+    <product productId="9">
+        <name>Phone G</name>
+        <color></color>
+        <description>Shoot a basket. Shoot a movie. This video phone is the perfect way to save and share life&#39;s playful moments. Feel connected.</description>
+        <price>329.99</price>
+        <series>6000</series>
+        <triband>false</triband>
+        <camera>true</camera>
+        <video>true</video>
+        <highlight1>Bluetooth technology</highlight1>
+		<highlight2>Up to 10 minutes video</highlight2>
+    </product>
+
+    <product productId="10">
+        <name>Phone H</name>
+        <color>Silver</color>
+        <description>Get creative with this smartphone. Create your own ringing tones, print your mobile images, play multiplayer games over a wireless Bluetooth connection, and browse HTML and xHTML Web pages. Plus, with a 32 MB MMC card and expandable memory, you&#39;ll have lots of space to be creative.</description>
+        <price>369</price>
+        <series>3000</series>
+        <triband>false</triband>
+        <camera>true</camera>
+        <video>true</video>
+        <highlight1>1.3 megapixel</highlight1>
+		<highlight2>Expandable memory</highlight2>
+    </product>
+
+    <product productId="11">
+        <name>Phone I</name>
+        <color>Bronze</color>
+        <description>Get creative with this smartphone. Create your own ringing tones, print your mobile images, play multiplayer games over a wireless Bluetooth connection, and browse HTML and xHTML Web pages. Plus, with a 32 MB MMC card and expandable memory, you&#39;ll have lots of space to be creative.</description>
+        <price>369</price>
+        <series>3000</series>
+        <triband>false</triband>
+        <camera>true</camera>
+        <video>true</video>
+        <highlight1>1.3 megapixel</highlight1>
+		<highlight2>Expandable memory</highlight2>
+    </product>
+
+    <product productId="12">
+        <name>Phone J</name>
+        <color></color>
+        <description>Not only is this imaging smartphone have a 1.3 megapixel digital imaging device (1.3 megapixel camera sensor, effective resolution 1.23 megapixels for image capture, image size 1280 x 960 pixels), it&#39;s also a portable office and a modern rich-media machine.</description>
+        <price>379</price>
+        <series>6000</series>
+        <triband>false</triband>
+        <camera>true</camera>
+        <video>true</video>
+        <highlight1>1.3 megapixel</highlight1>
+		<highlight2>6x smooth digital zoom</highlight2>
+    </product>
+
+</catalog>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/flex-examples/blob/9343d391/tourdeflexmodules/src/spark/layouts/layouts/FilteredTileLayout.as
----------------------------------------------------------------------
diff --git a/tourdeflexmodules/src/spark/layouts/layouts/FilteredTileLayout.as b/tourdeflexmodules/src/spark/layouts/layouts/FilteredTileLayout.as
new file mode 100644
index 0000000..6c01c2c
--- /dev/null
+++ b/tourdeflexmodules/src/spark/layouts/layouts/FilteredTileLayout.as
@@ -0,0 +1,260 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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 layouts
+{
+	import mx.collections.ICollectionView;
+	import mx.effects.Parallel;
+	import mx.events.EffectEvent;
+	
+	import spark.components.supportClasses.GroupBase;
+	import spark.components.supportClasses.ItemRenderer;
+	import spark.effects.Fade;
+	import spark.effects.Move;
+	import spark.layouts.supportClasses.LayoutBase;
+
+	public class FilteredTileLayout extends LayoutBase
+	{
+		public var filteredItems:ICollectionView;
+		
+		public var fadeOutDuration:Number = 400;
+		
+		public var moveDuration:Number = 400;
+		
+		public var fadeInDuration:Number = 400;
+
+		private var _target:GroupBase;
+
+		private var _containerWidth:Number;
+		
+		private var fadeOutEffects:Parallel;
+		private var fadeInEffects:Parallel;
+		private var moveEffects:Parallel;
+		
+		private var _horizontalGap:Number = 10;
+
+		private var _verticalGap:Number = 10;
+
+		private var _tileWidth:Number = 100;
+
+		private var _tileHeight:Number = 100;
+
+		public function set	horizontalGap(value:Number):void
+		{
+			_horizontalGap = value;
+			if (target)	target.invalidateDisplayList();
+		}
+
+		public function set	verticalGap(value:Number):void
+		{
+			_verticalGap = value;
+			if (target)	target.invalidateDisplayList();
+		}
+		
+		public function set	tileWidth(value:Number):void
+		{
+			_tileWidth = value;
+			if (target)	target.invalidateDisplayList();
+		}
+		
+		public function set	tileHeight(value:Number):void
+		{
+			_tileHeight = value;
+			if (target)	target.invalidateDisplayList();
+		}
+		
+		public function filter():void
+		{
+			// Prevent updateDisplayList() from being executed while we are animating tiles
+			_target.autoLayout = false;
+
+			// No filter has been applied. Keep showing all the items in the dataProvider
+			if (filteredItems == null) return;
+			
+			var count:int = _target.numElements;
+			
+			// No items in the dataProvider: Nothing to show.
+			if (count == 0) return;
+			
+			var x:int = 0;
+			var y:int = 0;
+			
+			fadeOutEffects = new Parallel();
+			fadeInEffects = new Parallel();
+			moveEffects = new Parallel();
+
+			for (var i:int = 0; i < count; i++)
+			{
+				var itemRenderer:ItemRenderer = _target.getElementAt(i) as ItemRenderer;
+				
+				if (filteredItems.contains(itemRenderer.data))
+				{
+					// The element is part of the selection: calculate its x and y values
+					if (x + _tileWidth > _containerWidth)
+					{
+						x = 0;
+						y += _tileHeight + _verticalGap;
+					} 
+
+					if (itemRenderer.visible == false)
+					{
+						trace("FadeIn: " + itemRenderer.data.name);
+						// if the element was hidden, set its new x and y values (without Move animation) and register it for FadeIn animation
+						itemRenderer.visible = true;
+						itemRenderer.setLayoutBoundsPosition(x, y);
+						var fadeIn:Fade = new Fade(itemRenderer);
+						fadeIn.alphaTo = 1;
+						fadeInEffects.addChild(fadeIn);
+					}  
+					else
+					{
+						trace("Move: " + itemRenderer.data.name);
+						// the element was already visible: register it for Move animation
+						if (itemRenderer.x != x || itemRenderer.y != y)
+						{
+							var move:Move = new Move(itemRenderer);
+							move.xTo = x;
+							move.yTo = y;
+							moveEffects.addChild(move);
+						}
+					}
+					x += _tileWidth + _horizontalGap;
+				}					
+				else
+				{
+					if (itemRenderer.alpha == 1)
+					{
+						trace("FadeOut: " + itemRenderer.data.name);
+						// the element is filtered out: register it for FadeOut animation
+						var fadeOut:Fade = new Fade(itemRenderer);
+						fadeOut.alphaTo = 0;
+						fadeOutEffects.addChild(fadeOut);
+					}
+				}
+			}
+			fadeOutTiles();			
+		}
+
+		private function fadeOutTiles(event:EffectEvent = null):void
+		{
+			trace("fadeOutTiles");
+			if (fadeOutEffects.children.length > 0) {
+				fadeOutEffects.duration = fadeOutDuration;
+				fadeOutEffects.addEventListener(EffectEvent.EFFECT_END, moveTiles)
+				fadeOutEffects.play();
+			}
+			else
+			{
+				moveTiles();	
+			}
+		}
+		
+		private function moveTiles(event:EffectEvent = null):void
+		{
+			// Undesired behaviors may happen if we leave tiles with alpha=0 in the display list while performing other animations 
+			setInvisibleTiles();
+			
+			trace("moveTiles");
+			if (moveEffects.children.length > 0) {
+				moveEffects.duration = moveDuration;
+				moveEffects.addEventListener(EffectEvent.EFFECT_END, fadeInTiles)
+				moveEffects.play();
+			}
+			else
+			{
+				fadeInTiles();	
+			}
+		}
+
+		private function fadeInTiles(event:EffectEvent = null):void
+		{
+			trace("fadeInTiles");
+			if (fadeInEffects.children.length > 0) {
+				fadeInEffects.duration = fadeInDuration;
+				moveEffects.addEventListener(EffectEvent.EFFECT_END, fadeInTilesEnd)
+				fadeInEffects.play();
+			}
+			else
+			{
+				fadeInTilesEnd();	
+			}
+		}
+		
+		private function fadeInTilesEnd(event:EffectEvent = null):void
+		{
+			_target.autoLayout = true; 
+		}
+		
+		private function setInvisibleTiles():void
+		{
+			var count:int = _target.numElements;
+			
+			if (count == 0) return;
+			
+			for (var i:int = 0; i < count; i++)
+			{
+				var itemRenderer:ItemRenderer = _target.getElementAt(i) as ItemRenderer;
+				if (!filteredItems.contains(itemRenderer.data))
+				{	
+					trace("Removing from layout: " + itemRenderer.data.name);					
+					itemRenderer.visible = false;
+				}		
+			}
+		}
+	
+		override public function updateDisplayList(containerWidth:Number, containerHeight:Number):void
+		{
+			trace("updateDisplaylist");
+
+			_target = target;
+			_containerWidth = containerWidth;
+
+			var count:int = target.numElements;
+			if (count == 0) return;
+			
+			var x:int=0;
+			var y:int=0;
+			
+			for (var i:int = 0; i < count; i++)
+			{
+				var itemRenderer:ItemRenderer = _target.getElementAt(i) as ItemRenderer;
+
+				itemRenderer.setLayoutBoundsSize(_tileWidth, _tileHeight);
+				
+				if (filteredItems && filteredItems.contains(itemRenderer.data))
+				{
+					// The element is part of the selection: calculate its x and y values
+					if (x + _tileWidth > containerWidth)
+					{
+						x = 0;
+						y += _tileHeight + _verticalGap;
+					} 
+					itemRenderer.setLayoutBoundsPosition(x, y);	
+					itemRenderer.alpha = 1;
+					x += _tileWidth + _horizontalGap;
+				}					
+				else
+				{
+					itemRenderer.alpha = 0;
+				}
+				
+			}
+		}
+
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/flex-examples/blob/9343d391/tourdeflexmodules/src/spark/layouts/renderers/PhoneRenderer.mxml
----------------------------------------------------------------------
diff --git a/tourdeflexmodules/src/spark/layouts/renderers/PhoneRenderer.mxml b/tourdeflexmodules/src/spark/layouts/renderers/PhoneRenderer.mxml
new file mode 100644
index 0000000..a6080c0
--- /dev/null
+++ b/tourdeflexmodules/src/spark/layouts/renderers/PhoneRenderer.mxml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+  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.
+
+-->
+<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
+				xmlns:s="library://ns.adobe.com/flex/spark"
+				xmlns:mx="library://ns.adobe.com/flex/mx" 
+				autoDrawBackground="false" width="100" height="100" clipAndEnableScrolling="false"
+				depth="0" depth.hovered="1">
+	
+	<fx:Declarations>
+		<mx:CurrencyFormatter id="cf"/>
+	</fx:Declarations>
+	
+	<s:states>
+		<s:State name="normal" />
+		<s:State name="hovered" />
+	</s:states>
+
+	<s:transitions>
+		<s:Transition fromState="normal" toState="hovered" autoReverse="true">
+				<s:Parallel>
+					<s:Rotate3D target="{image}" angleXFrom="0" angleXTo="0" angleZFrom="0" autoCenterTransform="true" angleYTo="360" angleYFrom="0" autoCenterProjection="true" angleZTo="0"/>
+					<s:Fade target="{group}" startDelay="320"/>
+				</s:Parallel>
+		</s:Transition>
+	</s:transitions>
+	
+	<mx:Image id="image" source="@Embed('../assets/ApacheFlexIcon.png')" horizontalCenter="0" />
+
+	<s:Group id="group" top="0" bottom="0" left="0" right="0" visible.normal="false">
+
+		<s:Rect id="rect" left="0" right="0" top="0" bottom="0" radiusX="4" radiusY="4">
+			<s:fill>
+				<s:SolidColor color="black" alpha="0.5"/>
+			</s:fill>
+		</s:Rect>
+		
+		<s:Label text="{data.name}" verticalCenter="-20" horizontalCenter="0" color="white"/> 
+		<s:Label text="{data.color}" verticalCenter="0" horizontalCenter="0" color="white"/> 
+		<s:Label text="{cf.format(data.price)}" verticalCenter="20" horizontalCenter="0" color="white"/>
+		
+	</s:Group>
+
+</s:ItemRenderer>

http://git-wip-us.apache.org/repos/asf/flex-examples/blob/9343d391/tourdeflexmodules/src/spark/modules/Module1.mxml
----------------------------------------------------------------------
diff --git a/tourdeflexmodules/src/spark/modules/Module1.mxml b/tourdeflexmodules/src/spark/modules/Module1.mxml
new file mode 100644
index 0000000..1ca400e
--- /dev/null
+++ b/tourdeflexmodules/src/spark/modules/Module1.mxml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+  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.
+
+-->
+<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml">
+    
+    <mx:Label text="this is module 1"/>
+    
+</mx:Module>

http://git-wip-us.apache.org/repos/asf/flex-examples/blob/9343d391/tourdeflexmodules/src/spark/modules/Module2.mxml
----------------------------------------------------------------------
diff --git a/tourdeflexmodules/src/spark/modules/Module2.mxml b/tourdeflexmodules/src/spark/modules/Module2.mxml
new file mode 100644
index 0000000..948fac5
--- /dev/null
+++ b/tourdeflexmodules/src/spark/modules/Module2.mxml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+  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.
+
+-->
+<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml">
+    
+    <mx:Label text="this is module 2"/>
+    
+</mx:Module>

http://git-wip-us.apache.org/repos/asf/flex-examples/blob/9343d391/tourdeflexmodules/src/spark/modules/ModuleExample.mxml
----------------------------------------------------------------------
diff --git a/tourdeflexmodules/src/spark/modules/ModuleExample.mxml b/tourdeflexmodules/src/spark/modules/ModuleExample.mxml
new file mode 100644
index 0000000..681f37a
--- /dev/null
+++ b/tourdeflexmodules/src/spark/modules/ModuleExample.mxml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+  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.
+
+-->
+<s:Module xmlns:fx="http://ns.adobe.com/mxml/2009"  
+			   xmlns:s="library://ns.adobe.com/flex/spark" 
+			   xmlns:mx="library://ns.adobe.com/flex/mx">
+	
+	<fx:Script>
+		<![CDATA[
+			
+			private function loadModule(title:String, url:String):void
+			{
+				var p:ModulePanel = new ModulePanel();
+				p.title = title;
+				p.url = url;
+				hBox.addChild(p);
+			}
+			
+		]]>
+	</fx:Script>
+	
+	<fx:Declarations>
+		<fx:Component className="ModulePanel">
+			<s:Panel>
+				<fx:Script>
+					<![CDATA[
+						[Bindable]
+						public var url:String;
+					]]>
+				</fx:Script>
+				
+				<s:layout>
+					<s:VerticalLayout horizontalAlign="center" />
+				</s:layout>
+				
+				<mx:ModuleLoader id="ml" url="{url}" width="100%" height="100%">
+					<mx:unload>
+						if(parent != null)
+						{
+							parent.removeChild(this);
+						}
+					</mx:unload>
+				</mx:ModuleLoader>
+				<mx:ControlBar>
+					<s:Button label="unload" click="ml.unloadModule()"/>
+				</mx:ControlBar>
+			</s:Panel>
+		</fx:Component>
+	</fx:Declarations>
+	
+	<s:layout>
+		<s:VerticalLayout horizontalAlign="center"/>
+	</s:layout>
+	
+	<s:Panel title="Module Sample" width="100%" height="100%">
+
+		<s:layout>
+			<s:VerticalLayout paddingLeft="10" paddingRight="10" paddingTop="10" paddingBottom="10" />
+		</s:layout>
+		
+		<s:Button label="Load Module 1" click="loadModule('module 1', './spark/modules/Module1.swf')"/>
+		<s:Button label="Load Module 2" click="loadModule('module 2', './spark/modules/Module2.swf')"/>
+	
+		<mx:HBox id="hBox" />
+		
+	</s:Panel> 
+</s:Module>

http://git-wip-us.apache.org/repos/asf/flex-examples/blob/9343d391/tourdeflexmodules/src/spark/modules/TDFGradientBackgroundSkin.mxml
----------------------------------------------------------------------
diff --git a/tourdeflexmodules/src/spark/modules/TDFGradientBackgroundSkin.mxml b/tourdeflexmodules/src/spark/modules/TDFGradientBackgroundSkin.mxml
new file mode 100644
index 0000000..fe2a943
--- /dev/null
+++ b/tourdeflexmodules/src/spark/modules/TDFGradientBackgroundSkin.mxml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+  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.
+
+-->
+<s:SparkSkin xmlns:fx="http://ns.adobe.com/mxml/2009" 
+			 xmlns:mx="library://ns.adobe.com/flex/mx" 
+			 xmlns:s="library://ns.adobe.com/flex/spark">
+	
+	<fx:Metadata>
+		[HostComponent("spark.components.Application")]
+	</fx:Metadata> 
+	
+	<s:states>
+		<s:State name="normal" />
+		<s:State name="disabled" />
+	</s:states>
+	
+	<s:layout>
+		<s:BasicLayout />
+	</s:layout>
+	
+	<s:Rect id="bg" width="100%" height="100%">
+		<s:fill>
+			<s:LinearGradient rotation="90">
+				<s:entries>
+					<s:GradientEntry color="0x000000" ratio="0.00" />
+					<s:GradientEntry color="0x323232" ratio="1.0" />
+				</s:entries>
+			</s:LinearGradient>    
+		</s:fill>
+	</s:Rect>
+	
+	<s:Group id="contentGroup" left="0" right="0" top="0" bottom="0" />
+</s:SparkSkin>

http://git-wip-us.apache.org/repos/asf/flex-examples/blob/9343d391/tourdeflexmodules/src/spark/other/BidirectionalBinding1Example.mxml
----------------------------------------------------------------------
diff --git a/tourdeflexmodules/src/spark/other/BidirectionalBinding1Example.mxml b/tourdeflexmodules/src/spark/other/BidirectionalBinding1Example.mxml
new file mode 100644
index 0000000..a84a357
--- /dev/null
+++ b/tourdeflexmodules/src/spark/other/BidirectionalBinding1Example.mxml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+  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.
+
+-->
+<s:Module 
+	xmlns:fx="http://ns.adobe.com/mxml/2009" 
+	xmlns:s="library://ns.adobe.com/flex/spark" 
+	xmlns:mx="library://ns.adobe.com/flex/mx">
+	
+	<s:Panel width="100%" height="100%"
+			 title="Bidirectional Binding 1"
+			 horizontalCenter="0"
+			 >
+		<s:Label top="10" left="15" verticalAlign="justify" width="200"
+					  text="Flex 4 interprets the @ binding syntax differently than Flex 3 in 
+that it will now represent a two-way binding. In this example, the text2 TextInput is bound to text1 bidirectionally, 
+so any change to the text2 value will also update the text1 value. "/>
+			
+			<s:VGroup horizontalCenter="15" top="40">
+				<s:Label text="Enter Text:"/>
+				<s:TextInput id="text1" widthInChars="20" />
+				<s:TextInput id="text2" color="0xFF3366" widthInChars="20" text="@{text1.text}"/>
+			</s:VGroup>	
+		
+	</s:Panel>
+	
+</s:Module>

http://git-wip-us.apache.org/repos/asf/flex-examples/blob/9343d391/tourdeflexmodules/src/spark/other/BidirectionalBinding2Example.mxml
----------------------------------------------------------------------
diff --git a/tourdeflexmodules/src/spark/other/BidirectionalBinding2Example.mxml b/tourdeflexmodules/src/spark/other/BidirectionalBinding2Example.mxml
new file mode 100644
index 0000000..de03192
--- /dev/null
+++ b/tourdeflexmodules/src/spark/other/BidirectionalBinding2Example.mxml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+  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.
+
+-->
+<s:Module xmlns:fx="http://ns.adobe.com/mxml/2009" 
+			   xmlns:s="library://ns.adobe.com/flex/spark" 
+			   xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:local="*">
+
+	<fx:Script>
+		<![CDATA[
+			import mx.events.ListEvent;
+			
+			//[Bindable]
+			//private var contact:Contact = new Contact();
+			
+			protected function submitBtn_clickHandler(event:MouseEvent):void
+			{
+				// Note: this sample uses bidirectional data binding, so we do not have to 
+				// explicitly set the contact fields to save them! 
+				if (!contacts.contains(contact))
+					contacts.addItem(contact);
+				contact = new Contact();
+			}
+
+			protected function dg_itemClickHandler(event:ListEvent):void
+			{
+				contact = dg.selectedItem as Contact;
+			}
+
+		]]>
+	</fx:Script>
+
+	<fx:Declarations>
+		<s:ArrayCollection id="contacts"/>
+		<local:Contact id="contact"/>
+	</fx:Declarations>
+	
+	<s:Panel width="100%" height="100%" title="TabBar Sample with Bidirectional Binding">
+		
+		<s:layout>
+			<s:VerticalLayout paddingLeft="10" paddingRight="10" paddingTop="10" paddingBottom="10"/>
+		</s:layout>
+		
+		<s:TabBar id="tabs" dataProvider="{vs}" cornerRadius="4"/>
+		<mx:ViewStack id="vs" width="95%" height="85%" left="8" y="23">
+			<s:NavigatorContent label="Contact Info"  width="100%" height="100%">
+				<s:BorderContainer width="100%" height="100%" dropShadowVisible="false">
+					<mx:Form id="contactForm" defaultButton="{submitBtn}" width="100%" height="100%">
+						<mx:FormItem label="Name:">
+							<s:TextInput id="nameTxt" text="@{contact.name}"/>
+						</mx:FormItem>
+						<mx:FormItem label="Address:">
+							<s:TextInput id="address" text="@{contact.address}"/>
+						</mx:FormItem>
+						<mx:FormItem label="City:">
+							<s:TextInput id="city" text="@{contact.city}"/>
+						</mx:FormItem>
+						<mx:FormItem label="State:">
+							<s:TextInput id="state" text="@{contact.state}"/>
+						</mx:FormItem>
+						<mx:FormItem label="Zip:">
+							<s:TextInput id="zip" text="@{contact.zip}" maxChars="5"/>
+						</mx:FormItem>
+						<mx:FormItem>
+							<s:Button id="submitBtn" label="Submit" click="submitBtn_clickHandler(event)"/>
+						</mx:FormItem>
+					</mx:Form>
+				</s:BorderContainer>
+			</s:NavigatorContent>
+			<s:NavigatorContent label="Contact List" width="100%" height="100%" >
+				<s:BorderContainer width="100%" height="100%" dropShadowVisible="false">
+					<mx:DataGrid id="dg" dataProvider="{contacts}" x="5" y="5"  doubleClickEnabled="true" 
+								 doubleClick="{tabs.selectedIndex=0}" itemClick="dg_itemClickHandler(event)">
+						<mx:columns>
+							<mx:DataGridColumn headerText="Name" dataField="name"/>
+							<mx:DataGridColumn headerText="Address" dataField="address"/>
+							<mx:DataGridColumn headerText="City" dataField="city"/>
+							<mx:DataGridColumn headerText="State" dataField="state"/>
+							<mx:DataGridColumn headerText="Zip" dataField="zip"/>
+						</mx:columns>
+					</mx:DataGrid>
+				</s:BorderContainer>
+			</s:NavigatorContent>
+		</mx:ViewStack>
+	</s:Panel>
+</s:Module>