You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flex.apache.org by jm...@apache.org on 2014/08/26 15:17:48 UTC

[2/2] git commit: [flex-sdk] [refs/heads/develop] - FLEX-34476 Added RichTextEditor

FLEX-34476 Added RichTextEditor


Project: http://git-wip-us.apache.org/repos/asf/flex-sdk/repo
Commit: http://git-wip-us.apache.org/repos/asf/flex-sdk/commit/d4eeb05f
Tree: http://git-wip-us.apache.org/repos/asf/flex-sdk/tree/d4eeb05f
Diff: http://git-wip-us.apache.org/repos/asf/flex-sdk/diff/d4eeb05f

Branch: refs/heads/develop
Commit: d4eeb05f553348f2ee246758f2e86aced7831d74
Parents: 524cdbe
Author: Justin Mclean <jm...@apache.org>
Authored: Tue Aug 26 23:16:17 2014 +1000
Committer: Justin Mclean <jm...@apache.org>
Committed: Tue Aug 26 23:16:17 2014 +1000

----------------------------------------------------------------------
 .../projects/experimental/assets/tools/bold.png | Bin 0 -> 154 bytes
 .../experimental/assets/tools/bullet.png        | Bin 0 -> 156 bytes
 .../experimental/assets/tools/center.png        | Bin 0 -> 159 bytes
 .../experimental/assets/tools/italic.png        | Bin 0 -> 165 bytes
 .../experimental/assets/tools/justify.png       | Bin 0 -> 148 bytes
 .../projects/experimental/assets/tools/left.png | Bin 0 -> 163 bytes
 .../projects/experimental/assets/tools/link.png | Bin 0 -> 166 bytes
 .../experimental/assets/tools/right.png         | Bin 0 -> 160 bytes
 .../experimental/assets/tools/underline.png     | Bin 0 -> 167 bytes
 .../src/spark/components/RichTextEditor.as      | 226 ++++++++++++++++
 .../richTextEditorClasses/AlignTool.mxml        | 128 ++++++++++
 .../richTextEditorClasses/BoldTool.mxml         |  89 +++++++
 .../richTextEditorClasses/BulletTool.mxml       | 193 ++++++++++++++
 .../richTextEditorClasses/ColorTool.mxml        |  84 ++++++
 .../richTextEditorClasses/FontTool.mxml         |  96 +++++++
 .../richTextEditorClasses/ItalicTool.mxml       |  89 +++++++
 .../richTextEditorClasses/LinkTool.mxml         | 255 +++++++++++++++++++
 .../RichTextEditorToolBar.mxml                  | 105 ++++++++
 .../richTextEditorClasses/SizeTool.mxml         |  97 +++++++
 .../richTextEditorClasses/UnderlineTool.mxml    |  90 +++++++
 .../src/spark/skins/AlignToolSkin.mxml          |  86 +++++++
 21 files changed, 1538 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/assets/tools/bold.png
----------------------------------------------------------------------
diff --git a/frameworks/projects/experimental/assets/tools/bold.png b/frameworks/projects/experimental/assets/tools/bold.png
new file mode 100755
index 0000000..04abf6d
Binary files /dev/null and b/frameworks/projects/experimental/assets/tools/bold.png differ

http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/assets/tools/bullet.png
----------------------------------------------------------------------
diff --git a/frameworks/projects/experimental/assets/tools/bullet.png b/frameworks/projects/experimental/assets/tools/bullet.png
new file mode 100755
index 0000000..aa107ba
Binary files /dev/null and b/frameworks/projects/experimental/assets/tools/bullet.png differ

http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/assets/tools/center.png
----------------------------------------------------------------------
diff --git a/frameworks/projects/experimental/assets/tools/center.png b/frameworks/projects/experimental/assets/tools/center.png
new file mode 100755
index 0000000..68428a2
Binary files /dev/null and b/frameworks/projects/experimental/assets/tools/center.png differ

http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/assets/tools/italic.png
----------------------------------------------------------------------
diff --git a/frameworks/projects/experimental/assets/tools/italic.png b/frameworks/projects/experimental/assets/tools/italic.png
new file mode 100755
index 0000000..e89f768
Binary files /dev/null and b/frameworks/projects/experimental/assets/tools/italic.png differ

http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/assets/tools/justify.png
----------------------------------------------------------------------
diff --git a/frameworks/projects/experimental/assets/tools/justify.png b/frameworks/projects/experimental/assets/tools/justify.png
new file mode 100755
index 0000000..5ea6b2a
Binary files /dev/null and b/frameworks/projects/experimental/assets/tools/justify.png differ

http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/assets/tools/left.png
----------------------------------------------------------------------
diff --git a/frameworks/projects/experimental/assets/tools/left.png b/frameworks/projects/experimental/assets/tools/left.png
new file mode 100755
index 0000000..91bfa69
Binary files /dev/null and b/frameworks/projects/experimental/assets/tools/left.png differ

http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/assets/tools/link.png
----------------------------------------------------------------------
diff --git a/frameworks/projects/experimental/assets/tools/link.png b/frameworks/projects/experimental/assets/tools/link.png
new file mode 100755
index 0000000..f771236
Binary files /dev/null and b/frameworks/projects/experimental/assets/tools/link.png differ

http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/assets/tools/right.png
----------------------------------------------------------------------
diff --git a/frameworks/projects/experimental/assets/tools/right.png b/frameworks/projects/experimental/assets/tools/right.png
new file mode 100755
index 0000000..455cc65
Binary files /dev/null and b/frameworks/projects/experimental/assets/tools/right.png differ

http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/assets/tools/underline.png
----------------------------------------------------------------------
diff --git a/frameworks/projects/experimental/assets/tools/underline.png b/frameworks/projects/experimental/assets/tools/underline.png
new file mode 100755
index 0000000..7d892b1
Binary files /dev/null and b/frameworks/projects/experimental/assets/tools/underline.png differ

http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/src/spark/components/RichTextEditor.as
----------------------------------------------------------------------
diff --git a/frameworks/projects/experimental/src/spark/components/RichTextEditor.as b/frameworks/projects/experimental/src/spark/components/RichTextEditor.as
new file mode 100644
index 0000000..1507d9e
--- /dev/null
+++ b/frameworks/projects/experimental/src/spark/components/RichTextEditor.as
@@ -0,0 +1,226 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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 spark.components
+{
+	import flash.events.Event;
+	import flash.utils.Dictionary;
+	import flashx.textLayout.conversion.ConversionType;
+	import flashx.textLayout.conversion.TextConverter;
+	import flashx.textLayout.elements.TextFlow;
+	import spark.components.richTextEditorClasses.RichTextEditorToolBar;
+	import spark.events.TextOperationEvent;
+
+	// for asdoc
+	[Experimental]
+	[Event(name = "change", type = "flash.events.Event")]
+	[Style(name = "borderColor", inherit = "no", type = "unit")]
+	[Style(name = "focusColor", inherit = "yes", type = "unit")]
+	public class RichTextEditor extends Group
+	{
+		private var _htmlText:String;
+		private var _htmlTextChanged:Boolean = false;
+		private var _prompt:String = "";
+		private var _stylesChanged:Dictionary = new Dictionary;
+		private var _text:String;
+		private var _textArea:TextArea;
+		private var _textFlow:TextFlow;
+
+		public function RichTextEditor()
+		{
+			super();
+			this.textFlow = new TextFlow; //Prevents a stack trace that happends when you try to access the textflow on click.
+		}
+
+		[Bindable("change")]
+		/**
+		 *  The htmlText property is here for convenience. It converts the textFlow to TextConverter.TEXT_FIELD_HTML_FORMAT.
+		 */
+		public function get htmlText():String
+		{
+			if (_htmlTextChanged)
+			{
+				if (text == "")
+				{
+					_htmlText = "";
+				}
+				else
+				{
+					_htmlText = TextConverter.export(textFlow, TextConverter.TEXT_FIELD_HTML_FORMAT, ConversionType.STRING_TYPE) as String;
+				}
+				_htmlTextChanged = false;
+			}
+			return _htmlText;
+		}
+
+		/**
+		 *  The htmlText property is here for convenience. It converts the textFlow to TextConverter.TEXT_FIELD_HTML_FORMAT.
+		 */
+		public function set htmlText(value:String):void
+		{
+			if (htmlText != value)
+			{
+				_htmlText = value;
+				if (textFlow)
+				{
+					textFlow = TextConverter.importToFlow(_htmlText, TextConverter.TEXT_FIELD_HTML_FORMAT);
+				}
+			}
+		}
+
+		/**
+		 *  @private
+		 */
+		public function get prompt():String
+		{
+			return _prompt;
+		}
+
+		/**
+		 *  @private
+		 */
+		public function set prompt(value:String):void
+		{
+			_prompt = value;
+			if (_textArea)
+			{
+				_textArea.prompt = _prompt;
+			}
+		}
+
+		/**
+		 *  @private
+		 */
+		public override function styleChanged(styleProp:String):void
+		{
+			super.styleChanged(styleProp);
+			_stylesChanged[styleProp] = getStyle(styleProp);
+			this.invalidateDisplayList();
+		}
+
+		[Bindable("change")]
+		/**
+		 *  The text in the textArea
+		 */
+		public function get text():String
+		{
+			if (_textArea)
+			{
+				return _textArea.text;
+			}
+			else
+			{
+				return _text;
+			}
+		}
+
+		/**
+		 *  @private
+		 */
+		public function set text(value:String):void
+		{
+			_text = value;
+			if (_textArea)
+			{
+				_textArea.text = value;
+			}
+		}
+
+		[Bindable("change")]
+		/**
+		 *  The textFlow
+		 */
+		public function get textFlow():TextFlow
+		{
+			return _textFlow;
+		}
+
+		/**
+		 *  @private
+		 */
+		public function set textFlow(value:TextFlow):void
+		{
+			_textFlow = value;
+			if (_textArea)
+			{
+				_textArea.textFlow = value;
+			}
+		}
+
+		/**
+		 *  @private
+		 */
+		protected override function createChildren():void
+		{
+			super.createChildren();
+			var container:VGroup = new VGroup;
+			container.percentHeight = 100;
+			container.percentWidth = 100;
+			this.addElement(container);
+
+			var toolbar:RichTextEditorToolBar = new RichTextEditorToolBar();
+			toolbar.percentWidth = 100;
+			toolbar.bottom = 6;
+			container.addElement(toolbar);
+
+			_textArea = new TextArea();
+			_textArea.percentHeight = 100;
+			_textArea.percentWidth = 100;
+			_textArea.addEventListener(TextOperationEvent.CHANGE, handleChange);
+			_textArea.prompt = prompt;
+			_textArea.textFlow = textFlow;
+			if (_htmlText)
+			{
+				textFlow = TextConverter.importToFlow(_htmlText, TextConverter.TEXT_FIELD_HTML_FORMAT);
+			}
+			else if (_text)
+			{
+				_textArea.text = _text;
+			}
+			container.addElement(_textArea);
+
+			toolbar.textArea = _textArea;
+		}
+
+		/**
+		 *  @private
+		 */
+		protected override function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
+		{
+			super.updateDisplayList(unscaledWidth, unscaledHeight);
+			if (_textArea)
+			{
+				for (var key:String in _stylesChanged)
+				{
+					_textArea.setStyle(key, _stylesChanged[key]);
+				}
+				_stylesChanged = new Dictionary; //Clear it out
+			}
+		}
+
+		/**
+		 *  @private
+		 */
+		private function handleChange(e:Event):void
+		{
+			_htmlTextChanged = true;
+			this.dispatchEvent(e);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/AlignTool.mxml
----------------------------------------------------------------------
diff --git a/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/AlignTool.mxml b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/AlignTool.mxml
new file mode 100644
index 0000000..20f9c5f
--- /dev/null
+++ b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/AlignTool.mxml
@@ -0,0 +1,128 @@
+<?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:ButtonBar width="80" arrowKeysWrapFocus="true" change="handleChange(event);" mouseFocusEnabled="false" 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:Script>
+		<![CDATA[
+			import flashx.textLayout.formats.TextAlign;
+			import flashx.textLayout.formats.TextLayoutFormat;
+			import mx.collections.ArrayList;
+			import mx.events.FlexEvent;
+			import spark.components.TextArea;
+			import spark.events.TextOperationEvent;
+
+			[Embed(source = "../../../../assets/tools/center.png")]
+			private const CENTER:Class;
+			[Embed(source = "../../../../assets/tools/justify.png")]
+			private const JUSTIFY:Class;
+			[Embed(source = "../../../../assets/tools/left.png")]
+			private const LEFT:Class;
+			[Embed(source = "../../../../assets/tools/right.png")]
+			private const RIGHT:Class;
+
+			private var _textArea:TextArea;
+
+			/**
+			 *  The textArea that this component interacts with
+			 */
+			public function get textArea():TextArea
+			{
+				return _textArea;
+			}
+
+			/**
+			 *  @private
+			 */
+			public function set textArea(value:TextArea):void
+			{
+				if (_textArea)
+				{
+					_textArea.removeEventListener(FlexEvent.SELECTION_CHANGE, handleSelectionChange);
+				}
+				_textArea = value;
+				if (_textArea)
+				{
+					_textArea.addEventListener(FlexEvent.SELECTION_CHANGE, handleSelectionChange, false, 0, true);
+					handleSelectionChange();
+				}
+			}
+
+			/**
+			 *  @private
+			 */
+			protected override function createChildren():void
+			{
+				super.createChildren();
+
+				var dp:ArrayList = new ArrayList();
+				dp.addItem({icon: LEFT, toolTip: "Left align text", value: TextAlign.LEFT});
+				dp.addItem({icon: CENTER, toolTip: "Left align text", value: TextAlign.CENTER});
+				dp.addItem({icon: RIGHT, toolTip: "Left align text", value: TextAlign.RIGHT});
+				dp.addItem({icon: JUSTIFY, toolTip: "Left align text", value: TextAlign.JUSTIFY});
+				this.dataProvider = dp;
+			}
+
+			/**
+			 *  @private
+			 */
+			private function handleChange(e:Event):void
+			{
+				if (this.selectedItem)
+				{
+					var txtLayFmt:TextLayoutFormat = _textArea.getFormatOfRange(null, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition);
+					txtLayFmt.textAlign = this.selectedItem.value;
+					_textArea.setFormatOfRange(txtLayFmt, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition);
+					_textArea.setFocus();
+					_textArea.dispatchEvent(new TextOperationEvent(TextOperationEvent.CHANGE));
+				}
+			}
+
+			/**
+			 *  @private
+			 */
+			private function handleSelectionChange(e:FlexEvent = null):void
+			{
+				var format:TextLayoutFormat = _textArea.getFormatOfRange(null, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition);
+
+				switch (format.textAlign)
+				{
+					case TextAlign.LEFT:
+						this.selectedIndex = 0;
+						break;
+					case TextAlign.CENTER:
+						this.selectedIndex = 1;
+						break;
+					case TextAlign.RIGHT:
+						this.selectedIndex = 2;
+						break;
+					case TextAlign.JUSTIFY:
+						this.selectedIndex = 3;
+						break;
+					default:
+						this.selectedIndex = -1;
+						break;
+				}
+			}
+		]]>
+	</fx:Script>
+	<fx:Declarations>
+		<!-- Place non-visual elements (e.g., services, value objects) here -->
+	</fx:Declarations>
+</s:ButtonBar>

http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/BoldTool.mxml
----------------------------------------------------------------------
diff --git a/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/BoldTool.mxml b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/BoldTool.mxml
new file mode 100644
index 0000000..8e1e89d
--- /dev/null
+++ b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/BoldTool.mxml
@@ -0,0 +1,89 @@
+<?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:ToggleButton width="20" click="handleClick(event);" icon="@Embed('../../../../assets/tools/bold.png')" mouseFocusEnabled="false" toolTip="Bold text" 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:Script>
+		<![CDATA[
+			import flash.text.engine.FontWeight;
+			import flashx.textLayout.formats.TextLayoutFormat;
+			import mx.events.FlexEvent;
+			import spark.components.TextArea;
+			import spark.events.TextOperationEvent;
+
+			private var _textArea:TextArea;
+
+			/**
+			 *  The textArea that this component interacts with
+			 */
+			public function get textArea():TextArea
+			{
+				return _textArea;
+			}
+
+			/**
+			 *  @private
+			 */
+			public function set textArea(value:TextArea):void
+			{
+				if (_textArea)
+				{
+					_textArea.removeEventListener(FlexEvent.SELECTION_CHANGE, handleSelectionChange);
+				}
+				_textArea = value;
+				if (_textArea)
+				{
+					_textArea.addEventListener(FlexEvent.SELECTION_CHANGE, handleSelectionChange, false, 0, true);
+					handleSelectionChange();
+				}
+			}
+
+			/**
+			 *  @private
+			 */
+			private function handleClick(e:MouseEvent):void
+			{
+				var format:TextLayoutFormat = _textArea.getFormatOfRange(null, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition);
+				format.fontWeight = (format.fontWeight == FontWeight.BOLD) ? FontWeight.NORMAL : FontWeight.BOLD;
+				_textArea.setFormatOfRange(format, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition);
+				_textArea.setFocus();
+				_textArea.dispatchEvent(new TextOperationEvent(TextOperationEvent.CHANGE));
+			}
+
+			/**
+			 *  @private
+			 */
+			private function handleSelectionChange(e:FlexEvent = null):void
+			{
+				var format:TextLayoutFormat = _textArea.getFormatOfRange(null, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition);
+				if (format.fontWeight == FontWeight.BOLD)
+				{
+					this.selected = true;
+				}
+				else
+				{
+					this.selected = false;
+				}
+			}
+		]]>
+	</fx:Script>
+	<fx:Declarations>
+		<!-- Place non-visual elements (e.g., services, value objects) here -->
+	</fx:Declarations>
+</s:ToggleButton>

http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/BulletTool.mxml
----------------------------------------------------------------------
diff --git a/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/BulletTool.mxml b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/BulletTool.mxml
new file mode 100644
index 0000000..ca93cd8
--- /dev/null
+++ b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/BulletTool.mxml
@@ -0,0 +1,193 @@
+<?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:ToggleButton width="20" click="handleClick(event);" icon="@Embed('../../../../assets/tools/bullet.png')" mouseFocusEnabled="false" toolTip="Bullet points" 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:Script>
+		<![CDATA[
+			import flash.text.engine.FontWeight;
+			import flashx.textLayout.edit.IEditManager;
+			import flashx.textLayout.edit.ISelectionManager;
+			import flashx.textLayout.edit.SelectionState;
+			import flashx.textLayout.elements.FlowGroupElement;
+			import flashx.textLayout.elements.FlowLeafElement;
+			import flashx.textLayout.elements.ListElement;
+			import flashx.textLayout.formats.TextLayoutFormat;
+			import mx.events.FlexEvent;
+			import spark.components.TextArea;
+			import spark.events.TextOperationEvent;
+
+			private var _textArea:TextArea;
+
+			/**
+			 *  The textArea that this component interacts with
+			 */
+			public function get textArea():TextArea
+			{
+				return _textArea;
+			}
+
+			/**
+			 *  @private
+			 */
+			public function set textArea(value:TextArea):void
+			{
+				if (_textArea)
+				{
+					_textArea.removeEventListener(FlexEvent.SELECTION_CHANGE, handleSelectionChange);
+				}
+				_textArea = value;
+				if (_textArea)
+				{
+					_textArea.addEventListener(FlexEvent.SELECTION_CHANGE, handleSelectionChange, false, 0, true);
+					handleSelectionChange();
+				}
+			}
+
+			/**
+			 *  @private
+			 */
+			private function getSelectionState():SelectionState
+			{
+				if (_textArea.textFlow)
+				{
+					var selectionManager:ISelectionManager = _textArea.textFlow.interactionManager;
+					var selectionState:SelectionState = selectionManager.getSelectionState();
+					var startleaf:FlowLeafElement = _textArea.textFlow.findLeaf(selectionState.absoluteStart);
+					var endleaf:FlowLeafElement = _textArea.textFlow.findLeaf(selectionState.absoluteEnd);
+					selectionState.absoluteStart = startleaf.getAbsoluteStart();
+					selectionState.absoluteEnd = endleaf.getAbsoluteStart() + endleaf.parentRelativeEnd - endleaf.parentRelativeStart;
+					return selectionState;
+				}
+				return null;
+			}
+
+			/**
+			 *  @private
+			 */
+			private function handleClick(e:MouseEvent):void
+			{
+				if (_textArea.textFlow && _textArea.textFlow.interactionManager is IEditManager)
+				{
+					var editManager:IEditManager = IEditManager(_textArea.textFlow.interactionManager);
+					var doCreate:Boolean = true;
+					var selectionState:SelectionState = getSelectionState();
+					var listElements:Array = _textArea.textFlow.getElementsByTypeName("list");
+					for each (var listElement:ListElement in listElements)
+					{
+						var start:int = listElement.getAbsoluteStart();
+						var end:int = listElement.getAbsoluteStart() + listElement.parentRelativeEnd - listElement.parentRelativeStart;
+						if (selectionState.absoluteStart == start && selectionState.absoluteEnd == end)
+						{ //Same
+							removeList(listElement);
+							doCreate = false;
+							break;
+						}
+						else if (selectionState.absoluteStart == start && selectionState.absoluteEnd <= end)
+						{ //Inside touching start
+							selectionState = new SelectionState(_textArea.textFlow, end, selectionState.absoluteEnd);
+							removeList(listElement);
+							editManager.createList(null, null, selectionState);
+							doCreate = false;
+							break;
+						}
+						else if (selectionState.absoluteStart >= start && selectionState.absoluteEnd == end)
+						{ //Inside touching end
+							selectionState = new SelectionState(_textArea.textFlow, selectionState.absoluteStart, start);
+							removeList(listElement);
+							editManager.createList(null, null, selectionState);
+							doCreate = false;
+							break;
+						}
+						else if (selectionState.absoluteStart >= start && selectionState.absoluteEnd <= end)
+						{ //Inside
+							var firstRange:SelectionState = new SelectionState(_textArea.textFlow, selectionState.absoluteStart, start);
+							var secondRange:SelectionState = new SelectionState(_textArea.textFlow, end, selectionState.absoluteEnd);
+							removeList(listElement);
+							editManager.createList(null, null, firstRange);
+							editManager.createList(null, null, secondRange);
+							doCreate = false;
+							break;
+						}
+						else if ((selectionState.absoluteStart >= start && selectionState.absoluteStart <= end) || (selectionState.absoluteEnd >= start && selectionState.absoluteEnd <= end))
+						{ //Overlap. Include this list in the selection
+							selectionState = new SelectionState(_textArea.textFlow, Math.min(start, selectionState.absoluteStart), Math.max(end, selectionState.absoluteEnd));
+							removeList(listElement);
+						}
+						else if (selectionState.absoluteStart <= start && selectionState.absoluteEnd >= end)
+						{ //surround. Remove this list since it will get added back in, only expanded.
+							removeList(listElement);
+						}
+					}
+					if (doCreate)
+					{
+						IEditManager(_textArea.textFlow.interactionManager).createList(null, null, selectionState);
+					}
+					_textArea.textFlow.interactionManager.setFocus();
+				}
+			}
+
+			/**
+			 *  @private
+			 */
+			private function handleSelectionChange(e:FlexEvent = null):void
+			{
+				if (_textArea.textFlow)
+				{
+					var willRemoveBulletsIfClicked:Boolean = false;
+					var selectionState:SelectionState = getSelectionState();
+					var listElements:Array = _textArea.textFlow.getElementsByTypeName("list");
+					for each (var listElement:ListElement in listElements)
+					{
+						var start:int = listElement.getAbsoluteStart();
+						var end:int = listElement.getAbsoluteStart() + listElement.parentRelativeEnd - listElement.parentRelativeStart;
+						if (selectionState.absoluteStart == start && selectionState.absoluteEnd == end)
+						{ //Same
+							willRemoveBulletsIfClicked = true;
+							break;
+						}
+						else if (selectionState.absoluteStart >= start && selectionState.absoluteEnd <= end)
+						{ //Inside
+							willRemoveBulletsIfClicked = true;
+							break;
+						}
+					}
+					this.selected = willRemoveBulletsIfClicked;
+
+				}
+
+			}
+
+			/**
+			 *  @private
+			 */
+			private function removeList(listElement:ListElement):void
+			{
+				var editManager:IEditManager = IEditManager(_textArea.textFlow.interactionManager);
+
+				var target:FlowGroupElement = listElement.parent;
+				var targetIndex:int = target.getChildIndex(listElement);
+				editManager.moveChildren(listElement, 0, listElement.numChildren, target, targetIndex);
+			}
+		]]>
+	</fx:Script>
+	<fx:Declarations>
+		<!-- Place non-visual elements (e.g., services, value objects) here -->
+	</fx:Declarations>
+</s:ToggleButton>

http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/ColorTool.mxml
----------------------------------------------------------------------
diff --git a/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/ColorTool.mxml b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/ColorTool.mxml
new file mode 100644
index 0000000..86d3006
--- /dev/null
+++ b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/ColorTool.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.
+
+-->
+<ns:ColorPicker height="21" width="20" choose="{handleChoose(event)}" mouseFocusEnabled="false" toolTip="Color text" xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:mx="library://ns.adobe.com/flex/mx"
+				xmlns:ns="http://flex.apache.org/experimental/ns" xmlns:s="library://ns.adobe.com/flex/spark">
+	<fx:Script>
+		<![CDATA[
+			import mx.events.FlexEvent;
+			
+			import spark.components.TextArea;
+			import spark.events.ColorChangeEvent;
+			import spark.events.TextOperationEvent;
+			
+			import flashx.textLayout.formats.TextLayoutFormat;
+
+			private var _textArea:TextArea;
+
+			/**
+			 *  The textArea that this component interacts with
+			 */
+			public function get textArea():TextArea
+			{
+				return _textArea;
+			}
+
+			/**
+			 *  @private
+			 */
+			public function set textArea(value:TextArea):void
+			{
+				if (_textArea)
+				{
+					_textArea.removeEventListener(FlexEvent.SELECTION_CHANGE, handleSelectionChange);
+				}
+				_textArea = value;
+				if (_textArea)
+				{
+					_textArea.addEventListener(FlexEvent.SELECTION_CHANGE, handleSelectionChange, false, 0, true);
+					handleSelectionChange();
+				}
+			}
+
+			/**
+			 *  @private
+			 */
+			private function handleChoose(e:ColorChangeEvent):void
+			{
+				var format:TextLayoutFormat = _textArea.getFormatOfRange(null, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition);
+				format.color = e.color
+				_textArea.setFormatOfRange(format, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition);
+				_textArea.setFocus();
+				_textArea.dispatchEvent(new TextOperationEvent(TextOperationEvent.CHANGE));
+			}
+
+			/**
+			 *  @private
+			 */
+			private function handleSelectionChange(e:FlexEvent = null):void
+			{
+				var format:TextLayoutFormat = _textArea.getFormatOfRange(null, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition);
+				this.selectedColor = format.color;
+			}
+		]]>
+	</fx:Script>
+	<fx:Declarations>
+		<!-- Place non-visual elements (e.g., services, value objects) here -->
+	</fx:Declarations>
+</ns:ColorPicker>

http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/FontTool.mxml
----------------------------------------------------------------------
diff --git a/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/FontTool.mxml b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/FontTool.mxml
new file mode 100644
index 0000000..f0eba89
--- /dev/null
+++ b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/FontTool.mxml
@@ -0,0 +1,96 @@
+<?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:DropDownList width="100%" change="callLater(handleChange, [event]);" dataProvider="{null}" maxWidth="120" minWidth="80" mouseFocusEnabled="false" 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:Script>
+		<![CDATA[
+			import flashx.textLayout.formats.TextLayoutFormat;
+			import mx.collections.ArrayList;
+			import mx.collections.IList;
+			import mx.events.FlexEvent;
+			import spark.components.TextArea;
+			import spark.events.TextOperationEvent;
+
+			private const _defaultDataProvider:ArrayList = new ArrayList(["_sans", "_serif", "_typewriter", "Arial", "Calibri", "Courier", "Courier New", "Geneva,Georgia", "Helvetica", "Times New Roman", "Times", "Trebuchet MS", "Verdana"]);
+			private var _textArea:TextArea;
+
+			public override function set dataProvider(value:IList):void
+			{
+				if (value == null)
+				{
+					super.dataProvider = _defaultDataProvider;
+				}
+				else
+				{
+					super.dataProvider = value;
+				}
+			}
+
+			/**
+			 *  The textArea that this component interacts with
+			 */
+			public function get textArea():TextArea
+			{
+				return _textArea;
+			}
+
+			/**
+			 *  @private
+			 */
+			public function set textArea(value:TextArea):void
+			{
+				if (_textArea)
+				{
+					_textArea.removeEventListener(FlexEvent.SELECTION_CHANGE, handleSelectionChange);
+				}
+				_textArea = value;
+				if (_textArea)
+				{
+					_textArea.addEventListener(FlexEvent.SELECTION_CHANGE, handleSelectionChange, false, 0, true);
+					handleSelectionChange();
+				}
+			}
+
+			/**
+			 *  @private
+			 */
+			private function handleChange(e:Event):void
+			{
+				var format:TextLayoutFormat = _textArea.getFormatOfRange(null, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition);
+				format.fontFamily = this.selectedItem;
+				_textArea.setFormatOfRange(format, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition);
+				_textArea.setFocus();
+				_textArea.dispatchEvent(new TextOperationEvent(TextOperationEvent.CHANGE));
+			}
+
+			/**
+			 *  @private
+			 */
+			private function handleSelectionChange(e:FlexEvent = null):void
+			{
+				var format:TextLayoutFormat = _textArea.getFormatOfRange(null, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition);
+				this.selectedItem = format.fontFamily;
+			}
+		]]>
+	</fx:Script>
+	<fx:Declarations>
+		<!-- Place non-visual elements (e.g., services, value objects) here -->
+	</fx:Declarations>
+</s:DropDownList>

http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/ItalicTool.mxml
----------------------------------------------------------------------
diff --git a/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/ItalicTool.mxml b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/ItalicTool.mxml
new file mode 100644
index 0000000..9c54159
--- /dev/null
+++ b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/ItalicTool.mxml
@@ -0,0 +1,89 @@
+<?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:ToggleButton width="20" click="handleClick(event);" icon="@Embed('../../../../assets/tools/italic.png')" mouseFocusEnabled="false" toolTip="Italic text" 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:Script>
+		<![CDATA[
+			import flash.text.engine.FontPosture;
+			import flashx.textLayout.formats.TextLayoutFormat;
+			import mx.events.FlexEvent;
+			import spark.components.TextArea;
+			import spark.events.TextOperationEvent;
+
+			private var _textArea:TextArea;
+
+			/**
+			 *  The textArea that this component interacts with
+			 */
+			public function get textArea():TextArea
+			{
+				return _textArea;
+			}
+
+			/**
+			 *  @private
+			 */
+			public function set textArea(value:TextArea):void
+			{
+				if (_textArea)
+				{
+					_textArea.removeEventListener(FlexEvent.SELECTION_CHANGE, handleSelectionChange);
+				}
+				_textArea = value;
+				if (_textArea)
+				{
+					_textArea.addEventListener(FlexEvent.SELECTION_CHANGE, handleSelectionChange, false, 0, true);
+					handleSelectionChange();
+				}
+			}
+
+			/**
+			 *  @private
+			 */
+			private function handleClick(e:MouseEvent):void
+			{
+				var format:TextLayoutFormat = _textArea.getFormatOfRange(null, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition);
+				format.fontStyle = (format.fontStyle == FontPosture.ITALIC) ? FontPosture.NORMAL : FontPosture.ITALIC;
+				_textArea.setFormatOfRange(format, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition);
+				_textArea.setFocus();
+				_textArea.dispatchEvent(new TextOperationEvent(TextOperationEvent.CHANGE));
+			}
+
+			/**
+			 *  @private
+			 */
+			private function handleSelectionChange(e:FlexEvent = null):void
+			{
+				var format:TextLayoutFormat = _textArea.getFormatOfRange(null, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition);
+				if (format.fontStyle == FontPosture.ITALIC)
+				{
+					this.selected = true;
+				}
+				else
+				{
+					this.selected = false;
+				}
+			}
+		]]>
+	</fx:Script>
+	<fx:Declarations>
+		<!-- Place non-visual elements (e.g., services, value objects) here -->
+	</fx:Declarations>
+</s:ToggleButton>

http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/LinkTool.mxml
----------------------------------------------------------------------
diff --git a/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/LinkTool.mxml b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/LinkTool.mxml
new file mode 100644
index 0000000..49d9f04
--- /dev/null
+++ b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/LinkTool.mxml
@@ -0,0 +1,255 @@
+<?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:TextInput width="100%" text="" enabled="false" enter="apply(event)" keyDown="handleLinkKeydown(event)" minWidth="100" mouseFocusChange="apply(event)" mouseFocusEnabled="false"
+			 toolTip="Hyperlink text" 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>
+		[Event(name="linkSelectedChange", type="flash.events.Event")]
+	</fx:Metadata>
+	<fx:Script>
+		<![CDATA[
+			import flashx.textLayout.edit.ElementRange;
+			import flashx.textLayout.edit.IEditManager;
+			import flashx.textLayout.edit.SelectionState;
+			import flashx.textLayout.elements.LinkElement;
+			import flashx.textLayout.elements.ParagraphElement;
+			import flashx.textLayout.formats.TextLayoutFormat;
+			import mx.events.FlexEvent;
+			import spark.components.TextArea;
+
+			private var _linkSelected:Boolean = false;
+
+			private var _textArea:TextArea;
+			private var _urlRegExpression:RegExp = new RegExp("^(https?://(www\\.)?|www\\.)[-._~:/?#\\[\\]@!$&'()*+,;=a-z0-9]+$", 'i');
+			private const defaultLinkText:String = "http://";
+			private var lastRange:ElementRange;
+			private var _linkEl:LinkElement
+
+			[Bindable("linkSelectedChange")]
+			public function get linkSelected():Boolean
+			{
+				return _linkSelected;
+			}
+
+			/**
+			 *  The textArea that this component interacts with
+			 */
+			public function get textArea():TextArea
+			{
+				return _textArea;
+			}
+
+			/**
+			 *  @private
+			 */
+			public function set textArea(value:TextArea):void
+			{
+				if (_textArea)
+				{
+					_textArea.removeEventListener(FlexEvent.SELECTION_CHANGE, handleSelectionChange);
+					_textArea.removeEventListener(KeyboardEvent.KEY_DOWN, handleKeyDown);
+				}
+				_textArea = value;
+				if (_textArea)
+				{
+					_textArea.addEventListener(FlexEvent.SELECTION_CHANGE, handleSelectionChange, false, 0, true);
+					_textArea.addEventListener(KeyboardEvent.KEY_DOWN, handleKeyDown, false, 0, true);
+					handleSelectionChange();
+				}
+			}
+
+			/**
+			 *  @private
+			 *  Apply the link to the selected text
+			 */
+			private function apply(e:Event = null):void
+			{
+				var urlText:String = this.text == defaultLinkText ? '' : this.text;
+				applyLink(urlText, "_blank", true);
+				//Set focus to textFlow
+				textArea.textFlow.interactionManager.setFocus();
+			}
+
+			/**
+			 *  @private
+			 *  Actually apply the link to the selection. Repair the formating in the process.
+			 */
+			private function applyLink(href:String, target:String = null, extendToLinkBoundary:Boolean = false, operationState:SelectionState = null):void
+			{
+				if (textArea && textArea.textFlow && textArea.textFlow.interactionManager is IEditManager)
+				{
+					//Get the current format
+					var txtLayFmt:TextLayoutFormat = textArea.textFlow.interactionManager.getCommonCharacterFormat();
+					//Set the link
+					var operationState:SelectionState = null;
+					if (_linkEl != null)
+					{
+						operationState = new SelectionState(textArea.textFlow, _linkEl.getAbsoluteStart(), _linkEl.getAbsoluteStart() + _linkEl.textLength);
+					}
+					var linkElement:LinkElement = IEditManager(textArea.textFlow.interactionManager).applyLink(href, target, extendToLinkBoundary, operationState);
+					//Fix the formatting
+					if(linkElement)
+					{
+						IEditManager(textArea.textFlow.interactionManager).clearFormatOnElement(linkElement.getChildAt(0), txtLayFmt);
+					}
+					var selectionEnd:int = Math.max(textArea.selectionActivePosition, textArea.selectionAnchorPosition);
+					textArea.selectRange(selectionEnd, selectionEnd);
+					IEditManager(textArea.textFlow.interactionManager).applyLeafFormat(txtLayFmt);
+				}
+			}
+
+			/**
+			 *  @private
+			 *  Automatically add a link if the previous text looks like a link
+			 */
+			private function checkLinks():void
+			{
+				var position:int = _textArea.selectionActivePosition;
+				//Find the firt non-whitespace character
+				while (position > 0)
+				{
+					if (!isWhitespace(_textArea.textFlow.getCharCodeAtPosition(position)))
+					{
+						break;
+					}
+					position--;
+				}
+				//Find the next whitespace character
+				while (position > 0)
+				{
+					if (isWhitespace(_textArea.textFlow.getCharCodeAtPosition(position)))
+					{
+						position++; //Back up one character
+						break;
+					}
+					position--;
+				}
+				var testText:String = _textArea.textFlow.getText(position, _textArea.selectionActivePosition);
+				var result:Array = testText.match(_urlRegExpression);
+				if (result != null && result.length > 0)
+				{
+					if (_textArea.textFlow.interactionManager is IEditManager)
+					{
+						var selectionState:SelectionState = new SelectionState(_textArea.textFlow, position, _textArea.selectionActivePosition);
+						if (testText.substr(0, 3) == "www")
+						{
+							testText = "http://" + testText; //Add a missing 'http://' if needed
+						}
+						applyLink(testText, "_blank", true, selectionState);
+						_textArea.setFocus();
+					}
+				}
+			}
+
+			/**
+			 *  @private
+			 */
+			private function handleKeyDown(e:KeyboardEvent):void
+			{
+				if (e.keyCode == Keyboard.ENTER || e.keyCode == Keyboard.SPACE || e.keyCode == Keyboard.TAB)
+				{
+					checkLinks();
+				}
+			}
+
+			/**
+			 *  @private
+			 */
+			private function handleLinkKeydown(e:KeyboardEvent):void
+			{
+				e.stopImmediatePropagation();
+				if (e.keyCode == Keyboard.ENTER)
+				{
+					_textArea.setFocus();
+				}
+			}
+
+			/**
+			 *  @private
+			 *  Update the text display based on the selected range
+			 */
+			private function handleSelectionChange(e:FlexEvent = null):void
+			{
+				var selectionState:SelectionState = _textArea.textFlow.interactionManager.getSelectionState();
+				if (selectionState.absoluteStart != -1 && selectionState.absoluteEnd != -1)
+				{
+					var range:ElementRange = ElementRange.createElementRange(selectionState.textFlow, selectionState.absoluteStart, selectionState.absoluteEnd);
+					if (range)
+					{
+						var linkString:String = defaultLinkText;
+						_linkEl = range.firstLeaf.getParentByType(LinkElement) as LinkElement;
+						if (_linkEl != null)
+						{
+							var linkElStart:int = _linkEl.getAbsoluteStart();
+							var linkElEnd:int = linkElStart + _linkEl.textLength;
+							if (linkElEnd < linkElStart)
+							{
+								var temp:int = linkElStart;
+								linkElStart = linkElEnd;
+								linkElEnd = temp;
+							}
+
+							var beginRange:int = range.absoluteStart;
+							var endRange:int = range.absoluteEnd;
+
+							var beginPara:ParagraphElement = range.firstParagraph;
+							if (endRange == (beginPara.getAbsoluteStart() + beginPara.textLength))
+							{
+								endRange--;
+							}
+
+							if ((beginRange == endRange) || (endRange <= linkElEnd))
+							{
+								linkString = LinkElement(_linkEl).href;
+							}
+						}
+						var newLinkSelected:Boolean = _linkEl != null;
+						if (_linkSelected != newLinkSelected)
+						{
+							_linkSelected = newLinkSelected;
+							this.dispatchEvent(new Event("linkSelectedChange"));
+						}
+
+						this.text = linkString;
+
+						lastRange = range;
+					}
+					else
+					{
+						lastRange = null;
+					}
+				}
+				
+				enabled = _textArea.selectionAnchorPosition != _textArea.selectionActivePosition || _linkSelected;
+			}
+
+			/**
+			 *  @private
+			 *  Return true if the character is a whitespace character
+			 */
+			private function isWhitespace(charCode:uint):Boolean
+			{
+				return charCode === 0x0009 || charCode === 0x000A || charCode === 0x000B || charCode === 0x000C || charCode === 0x000D || charCode === 0x0020 || charCode === 0x0085 || charCode === 0x00A0 || charCode === 0x1680 || charCode === 0x180E || charCode === 0x2000 || charCode === 0x2001 || charCode === 0x2002 || charCode === 0x2003 || charCode === 0x2004 || charCode === 0x2005 || charCode === 0x2006 || charCode === 0x2007 || charCode === 0x2008 || charCode === 0x2009 || charCode === 0x200A || charCode === 0x2028 || charCode === 0x2029 || charCode === 0x202F || charCode === 0x205F || charCode === 0x3000;
+			}
+		]]>
+	</fx:Script>
+	<fx:Declarations>
+		<!-- Place non-visual elements (e.g., services, value objects) here -->
+	</fx:Declarations>
+</s:TextInput>

http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/RichTextEditorToolBar.mxml
----------------------------------------------------------------------
diff --git a/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/RichTextEditorToolBar.mxml b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/RichTextEditorToolBar.mxml
new file mode 100755
index 0000000..d7ef167
--- /dev/null
+++ b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/RichTextEditorToolBar.mxml
@@ -0,0 +1,105 @@
+<?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:HGroup gap="6" xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:richTextEditorClasses="spark.components.richTextEditorClasses.*"
+		  xmlns:s="library://ns.adobe.com/flex/spark">
+	<fx:Metadata>
+		[Event(name="linkSelectedChange", type="flash.events.Event")]
+	</fx:Metadata>
+	<fx:Script>
+		<![CDATA[
+			import mx.collections.IList;
+			import spark.components.TextArea;
+
+			[Bindable]
+			/**
+			 * A list of fonts for the font dropdown
+			 */
+			public var fonts:IList = null;
+
+			[Bindable]
+			/**
+			 * A list of sizes for the size dropdown
+			 */
+			public var sizes:IList = null;
+
+			private var _linkSelected:Boolean = false;
+			private var _textArea:TextArea;
+
+			[Bindable("linkSelectedChange")]
+			/**
+			 *  True if a link is currently selected
+			 */
+			public function get linkSelected():Boolean
+			{
+				return _linkSelected;
+			}
+
+			/**
+			 * The textArea that this toolbar is controlling
+			 */
+			[Bindable]
+			public function get textArea():TextArea
+			{
+				return _textArea;
+			}
+
+			/**
+			 *  @private
+			 */
+			public function set textArea(value:TextArea):void
+			{
+				_textArea = value;
+			}
+
+			/**
+			 *  @private
+			 */
+			private function handleLinkSelectedChange(e:Event):void
+			{
+				_linkSelected = (e.currentTarget as LinkTool).linkSelected;
+				this.dispatchEvent(new Event("linkSelectedChange"));
+			}
+		]]>
+	</fx:Script>
+	<fx:Declarations>
+		<!-- Place non-visual elements (e.g., services, value objects) here -->
+	</fx:Declarations>
+	<richTextEditorClasses:FontTool dataProvider="{fonts}" textArea="{textArea}"/>
+	<richTextEditorClasses:SizeTool dataProvider="{sizes}" textArea="{textArea}"/>
+	<s:HGroup gap="0">
+		<richTextEditorClasses:BoldTool textArea="{textArea}"/>
+		<richTextEditorClasses:ItalicTool textArea="{textArea}"/>
+		<richTextEditorClasses:UnderlineTool textArea="{textArea}"/>
+	</s:HGroup>
+	<richTextEditorClasses:ColorTool textArea="{textArea}"/>
+	<s:Line height="100%">
+		<s:stroke>
+			<s:SolidColorStroke color="#B3C2B8"/>
+		</s:stroke>
+	</s:Line>
+	<richTextEditorClasses:AlignTool textArea="{textArea}"/>
+	<richTextEditorClasses:BulletTool textArea="{textArea}"/>
+	<s:Line height="100%">
+		<s:stroke>
+			<s:SolidColorStroke color="#B3C2B8"/>
+		</s:stroke>
+	</s:Line>
+	<richTextEditorClasses:LinkTool linkSelectedChange="handleLinkSelectedChange(event)" textArea="{textArea}"/>
+</s:HGroup>

http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/SizeTool.mxml
----------------------------------------------------------------------
diff --git a/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/SizeTool.mxml b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/SizeTool.mxml
new file mode 100644
index 0000000..0f9b6e6
--- /dev/null
+++ b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/SizeTool.mxml
@@ -0,0 +1,97 @@
+<?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:DropDownList width="60" change="callLater(handleChange, [event]);" dataProvider="{null}" mouseFocusEnabled="false" 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:Script>
+		<![CDATA[
+			import flashx.textLayout.formats.TextLayoutFormat;
+			import mx.collections.ArrayList;
+			import mx.collections.IList;
+			import mx.events.FlexEvent;
+			import mx.utils.ArrayUtil;
+			import spark.components.TextArea;
+			import spark.events.TextOperationEvent;
+
+			private const _defaultDataProvider:ArrayList = new ArrayList([8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 36, 48, 72]);
+			private var _textArea:TextArea;
+
+			public override function set dataProvider(value:IList):void
+			{
+				if (value == null)
+				{
+					super.dataProvider = _defaultDataProvider;
+				}
+				else
+				{
+					super.dataProvider = value;
+				}
+			}
+
+			/**
+			 *  The textArea that this component interacts with
+			 */
+			public function get textArea():TextArea
+			{
+				return _textArea;
+			}
+
+			/**
+			 *  @private
+			 */
+			public function set textArea(value:TextArea):void
+			{
+				if (_textArea)
+				{
+					_textArea.removeEventListener(FlexEvent.SELECTION_CHANGE, handleSelectionChange);
+				}
+				_textArea = value;
+				if (_textArea)
+				{
+					_textArea.addEventListener(FlexEvent.SELECTION_CHANGE, handleSelectionChange, false, 0, true);
+					handleSelectionChange();
+				}
+			}
+
+			/**
+			 *  @private
+			 */
+			private function handleChange(e:Event):void
+			{
+				var format:TextLayoutFormat = _textArea.getFormatOfRange(null, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition);
+				format.fontSize = this.selectedItem;
+				_textArea.setFormatOfRange(format, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition);
+				_textArea.setFocus();
+				_textArea.dispatchEvent(new TextOperationEvent(TextOperationEvent.CHANGE));
+			}
+
+			/**
+			 *  @private
+			 */
+			private function handleSelectionChange(e:FlexEvent = null):void
+			{
+				var format:TextLayoutFormat = _textArea.getFormatOfRange(null, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition);
+				this.selectedItem = format.fontSize;
+			}
+		]]>
+	</fx:Script>
+	<fx:Declarations>
+		<!-- Place non-visual elements (e.g., services, value objects) here -->
+	</fx:Declarations>
+</s:DropDownList>

http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/UnderlineTool.mxml
----------------------------------------------------------------------
diff --git a/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/UnderlineTool.mxml b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/UnderlineTool.mxml
new file mode 100644
index 0000000..1ae0663
--- /dev/null
+++ b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/UnderlineTool.mxml
@@ -0,0 +1,90 @@
+<?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:ToggleButton width="20" click="handleClick(event);" icon="@Embed('../../../../assets/tools/underline.png')" mouseFocusEnabled="false" toolTip="Underline text"
+				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:Script>
+		<![CDATA[
+			import flash.text.engine.FontPosture;
+			import flashx.textLayout.formats.TextDecoration;
+			import flashx.textLayout.formats.TextLayoutFormat;
+			import mx.events.FlexEvent;
+			import spark.components.TextArea;
+			import spark.events.TextOperationEvent;
+
+			private var _textArea:TextArea;
+
+			/**
+			 *  The textArea that this component interacts with
+			 */
+			public function get textArea():TextArea
+			{
+				return _textArea;
+			}
+
+			/**
+			 *  @private
+			 */
+			public function set textArea(value:TextArea):void
+			{
+				if (_textArea)
+				{
+					_textArea.removeEventListener(FlexEvent.SELECTION_CHANGE, handleSelectionChange);
+				}
+				_textArea = value;
+				if (_textArea)
+				{
+					_textArea.addEventListener(FlexEvent.SELECTION_CHANGE, handleSelectionChange, false, 0, true);
+					handleSelectionChange();
+				}
+			}
+
+			/**
+			 *  @private
+			 */
+			private function handleClick(e:MouseEvent):void
+			{
+				var format:TextLayoutFormat = _textArea.getFormatOfRange(null, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition);
+				format.textDecoration = (format.textDecoration == TextDecoration.UNDERLINE) ? TextDecoration.NONE : TextDecoration.UNDERLINE;
+				_textArea.setFormatOfRange(format, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition);
+				_textArea.setFocus();
+				_textArea.dispatchEvent(new TextOperationEvent(TextOperationEvent.CHANGE));
+			}
+
+			/**
+			 *  @private
+			 */
+			private function handleSelectionChange(e:FlexEvent = null):void
+			{
+				var format:TextLayoutFormat = _textArea.getFormatOfRange(null, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition);
+				if (format.textDecoration == TextDecoration.UNDERLINE)
+				{
+					this.selected = true;
+				}
+				else
+				{
+					this.selected = false;
+				}
+			}
+		]]>
+	</fx:Script>
+	<fx:Declarations>
+		<!-- Place non-visual elements (e.g., services, value objects) here -->
+	</fx:Declarations>
+</s:ToggleButton>

http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/src/spark/skins/AlignToolSkin.mxml
----------------------------------------------------------------------
diff --git a/frameworks/projects/experimental/src/spark/skins/AlignToolSkin.mxml b/frameworks/projects/experimental/src/spark/skins/AlignToolSkin.mxml
new file mode 100644
index 0000000..36e58b3
--- /dev/null
+++ b/frameworks/projects/experimental/src/spark/skins/AlignToolSkin.mxml
@@ -0,0 +1,86 @@
+<?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.
+
+-->
+
+
+<!--- The default skin class for the Spark ButtonBar component. The buttons on the ButtonBar component
+	use the ButtonBarLastButtonSkin, ButtonBarFirstButtonSkin and ButtonBarMiddleButtonSkin classes.
+
+	  @see spark.components.ButtonBar
+	  @see spark.components.ButtonBarButton
+
+	  @langversion 3.0
+	  @playerversion Flash 10
+	  @playerversion AIR 1.5
+	  @productversion Flex 4
+-->
+<s:Skin alpha.disabled="0.5" xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark">
+
+	<fx:Metadata>
+		<![CDATA[ 
+       /** 
+         * @copy spark.skins.spark.ApplicationSkin#hostComponent
+         */
+        [HostComponent("spark.components.ButtonBar")]
+    ]]>
+	</fx:Metadata>
+
+	<s:states>
+		<s:State name="normal"/>
+		<s:State name="disabled"/>
+	</s:states>
+
+	<fx:Declarations>
+		<!---
+			@copy spark.components.ButtonBar#firstButton
+			@default spark.skins.spark.ButtonBarFirstButtonSkin
+			@see spark.skins.spark.ButtonBarFirstButtonSkin
+		-->
+		<fx:Component id="firstButton">
+			<s:ButtonBarButton skinClass="spark.skins.spark.ButtonBarFirstButtonSkin" toolTip="{data.toolTip}"/>
+		</fx:Component>
+
+		<!---
+			@copy spark.components.ButtonBar#middleButton
+			@default spark.skins.spark.ButtonBarMiddleButtonSkin
+			@see spark.skins.spark.ButtonBarMiddleButtonSkin
+		-->
+		<fx:Component id="middleButton">
+			<s:ButtonBarButton skinClass="spark.skins.spark.ButtonBarMiddleButtonSkin" toolTip="{data.toolTip}"/>
+		</fx:Component>
+
+		<!---
+			@copy spark.components.ButtonBar#lastButton
+			@default spark.skins.spark.ButtonBarLastButtonSkin
+			@see spark.skins.spark.ButtonBarLastButtonSkin
+		-->
+		<fx:Component id="lastButton">
+			<s:ButtonBarButton skinClass="spark.skins.spark.ButtonBarLastButtonSkin" toolTip="{data.toolTip}"/>
+		</fx:Component>
+
+	</fx:Declarations>
+
+	<!--- @copy spark.components.SkinnableDataContainer#dataGroup -->
+	<s:DataGroup id="dataGroup" height="100%" width="100%">
+		<s:layout>
+			<s:ButtonBarHorizontalLayout gap="-1"/>
+		</s:layout>
+	</s:DataGroup>
+
+</s:Skin>