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 <jmcl...@apache.org>
Authored: Tue Aug 26 23:16:17 2014 +1000
Committer: Justin Mclean <jmcl...@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>

Reply via email to