You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flex.apache.org by ah...@apache.org on 2014/04/25 07:27:10 UTC

[05/18] Squiggly spell checker donation from Adobe Systems Inc.

http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/a52655ac/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/SpellUI.as
----------------------------------------------------------------------
diff --git a/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/SpellUI.as b/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/SpellUI.as
new file mode 100644
index 0000000..426ca4c
--- /dev/null
+++ b/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/SpellUI.as
@@ -0,0 +1,562 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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 com.adobe.linguistics.spelling
+{
+	import com.adobe.linguistics.spelling.UserDictionary;
+	import com.adobe.linguistics.spelling.framework.ResourceTable;
+	import com.adobe.linguistics.spelling.framework.SpellingConfiguration;
+	import com.adobe.linguistics.spelling.framework.SpellingService;
+    import com.adobe.linguistics.spelling.ui.HaloHighlighter;
+    import com.adobe.linguistics.spelling.ui.HaloWordProcessor;
+    import com.adobe.linguistics.spelling.ui.IHighlighter;
+    import com.adobe.linguistics.spelling.ui.IWordProcessor;
+	import com.adobe.linguistics.utils.TextTokenizer;
+	import com.adobe.linguistics.utils.Token;
+	
+	import flash.events.Event;
+	import flash.events.FocusEvent;
+	import flash.geom.Point;
+	import flash.geom.Rectangle;
+	import flash.net.SharedObject;
+	import flash.net.URLLoader;
+	import flash.net.URLRequest;
+	import flash.text.TextField;
+	import flash.utils.Dictionary;
+	
+	import mx.controls.RichTextEditor;
+	import mx.controls.TextArea;
+	import mx.controls.TextInput;
+	import mx.core.UIComponent;
+	import mx.core.mx_internal;
+	import mx.events.ScrollEvent;
+	
+	use namespace mx_internal;
+	
+	/**
+	 * <p>A comprehensive convenience class that bundles a context menu UI, 
+	 * the spelling engine, the dictionary loader, and user dictionary support 
+	 * to enable a single-line integration of spell checking functionality into 
+	 * any text related Flex UI component.  While this class drastically simplifies 
+	 * the effort to enable spell checking functionality in a Flex UI component, 
+	 * it does shield the developer form controlling detailed behaviors of the spell 
+	 * checker and the user dictionary storage options. </p>
+	 *
+	 * <p>This class provides a simple UI for some standard Flex UI components.
+	 *	It is not intended to address a complete user interface.
+	 *	Instead, it presents a basic user interface for some commonly used Flex UI components.</p>
+	 *
+	 * <p>For advanced text editing applications, more complex features are likely required.
+	 *	For those applications, we recommend bypassing this class and utilizing the <code>SpellChecker</code> class directly.</p>
+	 *
+	 * <p><code>SpellUI</code> uses the AdobeSpellingConfig.xml file to lookup corresponding resource files for a given locale.
+	 * The default location of AdobeSpellingConfig.xml is [yourapplicationDirectory]/AdobeSpellingConfig.xml. This could be customized using 
+	 * <code>spellingConfigUrl</code> property of <code>SpellUI</code>. You don't have to change the content of this file. However,
+	 * if you want to add a new language, to use an alternative dictionary or to customize the location for your dictionaries, you can modify it. 
+	 * There's an known issue with IIS web server when loading dictionary files with unknown extensions, in which case you can modify the XML to work around it.</p><p>
+	 * A sample AdobeSpellingConfig.xml will look as follows:
+	 * <listing version="3.0">
+	 * <pre class="preWrapper">
+ 	 * &lt;?xml version=&quot;1.0&quot; encoding='UTF-8'?&gt;
+ 	 * &lt;SpellingConfig&gt;
+ 	 *   	&lt;LanguageResource language=&quot;English&quot; 	  languageCode=&quot;en_US&quot; ruleFile=&quot;dictionaries/en_US/en_US.aff&quot; dictionaryFile=&quot;dictionaries/en_US/en_US.dic&quot;/&gt;
+	 *    	&lt;LanguageResource language=&quot;Spanish&quot;    languageCode=&quot;es_ES&quot; ruleFile=&quot;dictionaries/es_ES/es_ES.aff&quot; dictionaryFile=&quot;dictionaries/es_ES/es_ES.dic&quot;/&gt;
+ 	 *   	&lt;LanguageResource language=&quot;Portuguese&quot; languageCode=&quot;pt_PT&quot; ruleFile=&quot;dictionaries/pt_PT/pt_PT.aff&quot; dictionaryFile=&quot;dictionaries/pt_PT/pt_PT.dic&quot;/&gt;
+ 	 *  	 &lt;LanguageResource language=&quot;Italian&quot; 	  languageCode=&quot;it_IT&quot; ruleFile=&quot;dictionaries/it_IT/it_IT.aff&quot; dictionaryFile=&quot;dictionaries/it_IT/it_IT.dic&quot;/&gt;
+ 	 * &lt;/SpellingConfig&gt;</pre>
+	 *
+	 * </listing>
+	 * Note: The languageCode can be an arbitrary value, as long as you are consistent when passing them to the Squiggly classes. 
+	 * However, we highly encourage you to follow the two part Unicode language identifier format. 
+	 * For more information, please consult the latest Unicode Technical Standard that can be found at: http://unicode.org/reports/tr35/.</p>
+	 * 
+	 * @playerversion Flash 10
+	 * @langversion 3.0
+	 */
+	public class SpellUI
+	{
+		private var hh:IHighlighter;
+		private var hw:IWordProcessor;
+				
+		
+		private var _checkLastWord:Boolean = true;
+		
+		private var _spellingEnabled:Boolean;
+		
+		private var _actualParent:*;
+		private var isHaloComponent:Boolean;
+
+		//New Added below
+		private var mTextField:TextField;
+				
+		private var _dictname:String = new String();	
+		
+		private var _userdict:UserDictionary = null;
+		private var _sharedobj:SharedObject = null;
+		private var scm:SpellingContextMenu;	
+		private var _newchecker:SpellChecker = null;
+		private var _resource_locale:Object = null;
+		private var _spellingservice:SpellingService = null;
+		
+		private static var _contextMenuEntries:Object = {enable:"Enable Spelling", disable:"Disable Spelling", add:"Add to dictionary"};
+		private static var _spellingConfigUrl:String = "AdobeSpellingConfig.xml";
+		
+		private static var _UITable:Dictionary= new Dictionary();
+		private static var _parentTable:Dictionary= new Dictionary();
+		private static var _cacheDictTable:Dictionary= new Dictionary();
+		
+		private static var _configXML:XML = null;
+		private static var _configXMLLoading:Boolean = false;
+		private static var _configXMLLoader:URLLoader = new URLLoader();
+		
+		
+		
+		// Work around for the memory usage problem, ideally a better fix is to provide a dicitonary unload function
+		private static var _cache:Object = new Object();
+
+		/**
+		 * Enable the spell checking feature in the specified language for a UI component. Once a component is spell checking enabled, misspelled words will be highlighted with a squiggly line. Users can 
+		 * right click on a misspelled word to see the suggestions in the context menu.
+		 * 
+		 *
+		 *
+		 * @param comp	A text editing Flex UI component. It can be a mx <code>TextArea</code>, <code>TextInput</code> or <code>RichTextEditor</code>. 
+		 * If you are using AdobeSpellingUIEx.swc, it can also be a spark <code>TextArea</code> or <code>TextInput</code>.
+		 * @param lang	The language code used for spell checking, for example <code>en_US</code>. it will lookup the AdobeSpellingConfig.xml file to access corresponding resource files.
+		 * AdobeSpellingConfig.xml should be located at the same folder as your main mxml source file. You don't have to change the content of this file. However,
+		 * if you want to add a new language, to use an alternative dictionary or to customize the location for your dictionaries, you can modify it. There's an known issue with
+		 * IIS web server when loading dictionary files with unknown extensions, in which case you can modify the XML to work around it.
+		 * 
+		 * 
+		 *
+		 * @includeExample Examples/Flex/SquigglyUIExample/src/SquigglyUIExample.mxml
+		 * @playerversion Flash 10
+		 * @langversion 3.0
+		 */
+		public static function enableSpelling(comp:UIComponent, lang:String):void
+		{
+			if ( lang == null ) return;
+		 	// TODO: Change dict parameter type to a SpellCheck class or a URL string.
+			var txt:* = getComponentTextModel(comp);
+			
+			if ( txt==null || _UITable[comp]!=undefined )
+				return;
+			
+			// TODO: dangerous, is garbage collection going to clear this?
+			_UITable[comp]=new SpellUI(txt, lang);
+			_parentTable[txt] = comp;
+			_cacheDictTable[comp]=lang;
+		}
+		
+		/**
+		 * Set the spelling context menu entries. This uses the flex object as an associative array for extensibility. 
+		 * <code>entries</code> should have all the customized contextMenu entries including <code>enable (spelling), 
+		 * disable (spelling) and add (to dictionary)</code>. To ensure a consistent contextMenu within your application, 
+		 * the spelling context menu entries you provide here are applied to all UI components. We recommend you use this API
+		 * with Flex resource for localization. Make sure to also have a look at the Flex localization documentation.
+		 * 
+		 * @see http://livedocs.adobe.com/flex/3/html/help.html?content=l10n_1.html 
+		 * 
+		 * @param entries A flex Ojbect that looks like <code>entries:Object = {enable:"Enable Spelling", disable:"Disable Spelling", 
+		 * add:"Add to dictionary"}</code>. If you don't customize the contextMenu, the default contextMenu in English will be used.
+		 * We recommend that you use the Flex localization resource to obtain these strings. 
+		 * @return <code>True</code> if the spelling menu is successfully customized, <code>false</code> if it fails. Possible failure 
+		 * reasons include passing the wrong object or missing some required entries. If the function fails, the contextMenu is left unchanged.
+		 * 
+		 * 
+		 * @includeExample Examples/Flex/CustomContextMenu/src/CustomContextMenu.mxml
+		 * @includeExample Examples/Flex/ContextMenuWithResource/src/ContextMenuWithResource.mxml
+		 * @playerversion Flash 10
+		 * @langversion 3.0
+		 */
+		public static function setSpellingMenuEntries(entries:Object):Boolean
+		{
+			if (entries.enable && entries.disable && entries.add && (entries.enable != "") && (entries.disable != "") && (entries.add != ""))
+			{
+					_contextMenuEntries = entries;
+					return true;
+			}
+			else
+				return false;
+		}
+
+		/**
+		 * Get the spelling context menu entries. 
+		 * 
+		 * @return A flex <code>Object</code> containing the spelling context menu entries. If you haven't customized the entries, you get the default associative array <code>{enable:"Enable Spelling", disable:"Disable Spelling", add:"Add to dictionary"}</code>
+		 * @playerversion Flash 10
+		 * @langversion 3.0
+		 */		
+		public static function getSpellingMenuEntries():Object
+		{
+			return _contextMenuEntries;
+		}
+			
+		/**
+		 * The URL for the spelling config xml file. If you haven't specified it, the default URL is [applicationDirectory]/AdobeSpellingConfig.xml. Note that we don't validate the URL, if the file doesn't exist, you will get an error when calling enableSpelling() function.
+		 *
+		 * @example The following code customize the spellingConfigUrl before enabling spell checking.
+		 * <listing version="3.0">
+		 * SpellUI.spellingConfigUrl = "./config/MySpellingConfig.xml";
+		 * SpellUI.enableSpelling(textArea, "es_ES");
+		 * </listing>
+		 */
+		public static function get spellingConfigURL():String
+		{
+			return _spellingConfigUrl;
+		}
+		
+		public static function set spellingConfigURL(url:String):void
+		{
+			if (url == null) throw new Error("URL can't be null");
+			_spellingConfigUrl = url;
+		}
+		
+
+			
+		/**
+		 * Disable the spell checking feature for a UI component.
+		 * 
+		 * @param comp	A text editing Flex UI component.
+		 * @playerversion Flash 10
+		 * @langversion 3.0
+		 */
+		public static function disableSpelling(comp:UIComponent):void{
+			if ( _UITable[comp] == undefined )
+				return;
+			var _ui:SpellUI = _UITable[comp];
+			if ( _ui != null) _ui.cleanUp();
+			var dictName:String = _cacheDictTable[comp];
+			var cleanUPDictionaryCount:int = 0;
+			for each ( var _dictName:String in _cacheDictTable ) {
+				if ( _dictName == dictName  )
+					cleanUPDictionaryCount++;
+			}
+			if ( cleanUPDictionaryCount == 1 ) {
+				_cache[dictName] = undefined;
+			}
+			delete _UITable[comp];
+			delete _cacheDictTable[comp];
+			
+		}
+		
+		/**
+		 @private
+		 (This property is for Squiggly Developer use only.)
+		 */
+		public static function get UITable():Dictionary {
+			return _UITable;
+		}
+		
+		/**
+		 @private
+		 (This property is for Squiggly Developer use only.)
+		 */
+		public function set spellingEnabled(value:Boolean):void {
+			_spellingEnabled = value;
+		}
+		
+		/**
+		 @private
+		 (This property is for Squiggly Developer use only.)
+		 */
+		public static function get parentComp():Dictionary {
+			return _parentTable;
+		}
+		
+		private static function getComponentTextModel(comp:UIComponent):* {
+			var txt:TextField = null;
+			if ( (comp == null) || !( (comp is TextArea) || (comp is TextInput) || (comp is RichTextEditor) ) )
+				return null;
+			if ((comp as RichTextEditor) != null) {
+				txt = (comp as RichTextEditor).textArea.getTextField() as TextField;
+			}
+			else if ((comp as TextArea) != null){
+				txt = (comp as TextArea).getTextField() as TextField;
+			}
+			else if ((comp as TextInput) != null) {
+				txt = (comp as TextInput).getTextField() as TextField;
+			}
+			else {
+				// do nothing if it's not a valid text component
+				return null;
+			}
+			return txt;
+		}
+		
+		/**
+		 * Constructs a SpellUI object.
+		 *	@private
+		 *
+		 *	@param	textFiled	A Flex UI component to include spell-check capability
+		 * 	@param lang	The language code used for spell checking
+		 *
+		 * @playerversion Flash 10
+		 * @langversion 3.0
+		 */			
+		public function SpellUI(textModel:*, lang:String)
+		{		
+			// TODO: Consider making this method invisible to user, only expose the static function.
+			if ( textModel is TextField ) {
+				isHaloComponent=true;
+			}else {
+				// do nothing, we only accept textField and TextFlow here....
+				return;
+			}
+			_actualParent = textModel;
+			
+			//New Added below -- check if text field needs to be extracted from textModel
+			mTextField = textModel;
+						
+			mTextField.addEventListener(FocusEvent.FOCUS_OUT, handleFocusOut);
+			mTextField.addEventListener(ScrollEvent.SCROLL, spellCheckScreen);
+			mTextField.parent.addEventListener(Event.RENDER, spellCheckScreen);
+			mTextField.parent.addEventListener(Event.CHANGE, handleChangeEvent);
+			_dictname = lang;			
+			loadConfig();
+			
+		}
+		
+		private function spellCheckScreen(event:Event):void
+		{
+			doSpellingJob();
+		}
+		
+		private function handleFocusOut(event:FocusEvent):void
+		{
+			_checkLastWord = true;
+			doSpellingJob();
+		}
+		
+		private function handleChangeEvent( event:Event ) :void {
+			_checkLastWord = false;
+			doSpellingJob();
+		}
+				
+		/*private function doSpelling():void
+		{
+			_checkLastWord = true;
+			doSpellingJob();
+		}*/
+		
+		/**
+		 @private
+		 (This property is for Squiggly Developer use only.)
+		 */		
+		public function doSpellingJob():void
+		{
+			if (_spellingEnabled == false) return;
+			spellCheckRange(getValidFirstWordIndex(), getValidLastWordIndex());
+		}
+		
+		private function spellCheckRange(start:uint, end:uint):void {
+			//hh.preSpellCheckRange(start, end);
+			hh.clearSquiggles();
+			
+			//if (end <= start) return;
+			var firstLine:int = mTextField.getLineIndexOfChar(start);
+			var rect:Rectangle = mTextField.getCharBoundaries(start);
+			var counter:uint = start;
+			var numLines:Number = 0;
+			
+			/* mTextField.getCharBoundaries returns null for blank lines and for end of line characters. Placing this workaround
+			to count line heights until a non-null bounding rectangle is found */
+			
+			while (rect == null) {
+				if (counter+1 > end) {
+						rect = new Rectangle(0,0,0,0);
+						break;
+					}				
+				if(mTextField.text.charCodeAt(counter)<0x0300 || mTextField.text.charCodeAt(counter)>0x036F)//not in diacritic combining range
+				{
+					numLines += mTextField.getLineMetrics(firstLine).height;
+					firstLine++;
+				}
+				counter++;
+				rect = mTextField.getCharBoundaries(counter);
+			}
+			
+			var yoffset:Number = rect.y - numLines;	
+			var pleft:uint = (mTextField.parent as UIComponent).getStyle("paddingLeft");
+			var ptop:uint = (mTextField.parent as UIComponent).getStyle("paddingTop");
+			
+			var offsetPoint:Point = new Point(pleft, ptop-yoffset); 
+						
+			var tokenizer:TextTokenizer = new TextTokenizer(mTextField.text.substring(start,end));
+			//var seps:Vector.<int> = new Vector.<int>();
+			//seps.push(new int(34));
+			//tokenizer.ignoredSeperators = seps;
+			var tokens:Vector.<Token> = new Vector.<Token>();
+			
+			hh.offsetPoint = offsetPoint;
+			
+			for ( var token:Token = tokenizer.getFirstToken(); token != tokenizer.getLastToken(); token= tokenizer.getNextToken(token) ) {
+				var result:Boolean=_spellingservice.checkWord(mTextField.text.substring(token.first+start, token.last+start));					
+				if (!result){
+					if (_checkLastWord || (token.last+start != mTextField.text.length)) 
+						//hh.highlightWord(token.first+start, token.last+start-1);
+						//tokens.push(new Token(token.first+start, token.last+start-1));
+						hh.drawSquiggleAt(new Token(token.first+start, token.last+start-1));
+				}
+				
+			}
+			//hh.postSpellCheckRange(start, end);
+			//hh.offsetPoint = offsetPoint;
+			//hh.drawSquiggles(tokens);
+		}
+		
+		
+		private function getValidFirstWordIndex():int{
+			return mTextField.getLineOffset(mTextField.scrollV-1);
+		}
+		
+		private function getValidLastWordIndex():int{
+			return mTextField.getLineOffset(mTextField.bottomScrollV-1)+mTextField.getLineLength(mTextField.bottomScrollV-1);
+		}
+
+		private function loadConfig():void{
+			_resource_locale = SpellingConfiguration.resourceTable.getResource(_dictname);
+			
+			if ((_resource_locale != null) || (SpellUI._configXML != null)) 
+				loadConfigComplete(null);
+			else {	
+				SpellUI._configXMLLoader.addEventListener(Event.COMPLETE, loadConfigComplete);
+			
+				if (SpellUI._configXMLLoading == false)
+				{
+					SpellUI._configXMLLoader.load(new URLRequest(_spellingConfigUrl));
+					SpellUI._configXMLLoading = true;
+				}
+			}
+		}
+		
+		private function loadConfigComplete(evt:Event):void{
+			if (_resource_locale == null) {
+				if (SpellUI._configXML == null)
+					SpellUI._configXML= new XML(evt.target.data);
+			
+				SpellingConfiguration.resourceTable.setResource(_dictname,{rule:SpellUI._configXML.LanguageResource.(@languageCode==_dictname).@ruleFile, 
+																		dict:SpellUI._configXML.LanguageResource.(@languageCode==_dictname).@dictionaryFile});
+			}
+			//New Added
+			_spellingservice = new SpellingService(_dictname);
+			_spellingservice.addEventListener(Event.COMPLETE, loadDictComplete);
+			_spellingservice.init();
+		}
+		
+
+		
+		
+		private function loadDictComplete(evt:Event):void
+		{					
+			
+			
+			// Lazy loading the UD only when the main dict is loaded successfully
+			if ((SpellUI._cache["Squiggly_UD"] as UserDictionary) == null)
+			{
+				_sharedobj = SharedObject.getLocal("Squiggly_v03");
+				var vec:Vector.<String> = new Vector.<String>();
+				if (_sharedobj.data.ud) {
+					for each (var w:String in _sharedobj.data.ud)
+						vec.push(w);
+				}
+				_userdict = new UserDictionary(vec);
+				
+				SpellUI._cache["Squiggly_SO"] = _sharedobj;
+				SpellUI._cache["Squiggly_UD"] = _userdict;
+			}
+			else 
+			{
+				_sharedobj = SpellUI._cache["Squiggly_SO"];
+				_userdict = SpellUI._cache["Squiggly_UD"];
+			}
+			_spellingservice.addUserDictionary(_userdict);
+			//Adding default behaviour to accomodate ignoring of abbreviations bug#2756840
+			_spellingservice.ignoreWordWithAllUpperCase=true;
+			
+			
+			// Add the context menu, this might be not successful
+			scm = null;
+			try {
+				addContextMenu(null);
+			}
+			catch (err:Error)
+			{
+				// TODO: error handling here
+			}
+			_actualParent.addEventListener(Event.ADDED_TO_STAGE, addContextMenu);
+		}
+		
+
+		private function addContextMenu(event:Event):void
+		{
+			if ( scm != null ) return;
+			if ( isHaloComponent ) {
+				hh= new HaloHighlighter( _actualParent);
+				hw= new HaloWordProcessor( _actualParent);
+			}else {
+				trace("error now, later will be true");
+			}
+		
+			scm =  new SpellingContextMenu(hh, hw, _spellingservice, _actualParent, _actualParent.contextMenu); 
+			scm.setIgnoreWordCallback( addWordToUserDictionary );
+			
+			// Halo need this
+			if (_actualParent.contextMenu == null)
+			{
+				_actualParent.contextMenu = scm.contextMenu;
+			}
+			
+			//hh.spellingEnabled=true;
+			_spellingEnabled = true;
+			try {
+				doSpellingJob();
+			}
+			catch (err:Error)
+			{
+				// If it fails here, it should later triggered by the render event, so no need to do anything
+			}
+			
+			
+		}
+		
+		private function addWordToUserDictionary(word:String):void
+		{
+			_userdict.addWord(word);
+			
+			// TODO: serialization here might affect ther performance
+			_sharedobj.data.ud = _userdict.wordList;
+			
+		}
+		/**
+		 *	@private
+		 */
+		private function cleanUp():void {
+			hh.clearSquiggles();
+			scm.cleanUp();
+			_actualParent.removeEventListener(Event.ADDED_TO_STAGE, addContextMenu);
+			
+			mTextField.removeEventListener(ScrollEvent.SCROLL, spellCheckScreen);
+			mTextField.parent.removeEventListener(Event.RENDER, spellCheckScreen);
+			mTextField.parent.removeEventListener(Event.CHANGE, handleChangeEvent);
+			mTextField.removeEventListener(FocusEvent.FOCUS_OUT, handleFocusOut);	
+		}
+		
+	}
+}

http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/a52655ac/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/SpellingContextMenu.as
----------------------------------------------------------------------
diff --git a/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/SpellingContextMenu.as b/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/SpellingContextMenu.as
new file mode 100644
index 0000000..d84f325
--- /dev/null
+++ b/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/SpellingContextMenu.as
@@ -0,0 +1,243 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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 com.adobe.linguistics.spelling
+{
+	import com.adobe.linguistics.spelling.framework.SpellingService;
+	import com.adobe.linguistics.utils.Token;
+	import com.adobe.linguistics.utils.TextTokenizer;
+	
+	import flash.events.ContextMenuEvent;
+	import flash.events.Event;
+	import flash.events.EventDispatcher;
+	
+	import flash.ui.ContextMenu;
+	import flash.ui.ContextMenuItem;
+	
+	import flash.text.TextField;
+	import mx.core.UIComponent;
+	
+	import flash.geom.Point;
+	import flash.geom.Rectangle;
+	import com.adobe.linguistics.spelling.ui.IHighlighter;
+	import com.adobe.linguistics.spelling.ui.IWordProcessor;
+	
+	public class SpellingContextMenu
+	{
+		private var disableMenuItem:ContextMenuItem = new ContextMenuItem("Disable spell checking",true);
+		private var enableMenuItem:ContextMenuItem = new ContextMenuItem("Enable spell checking");
+		private var controlMenuItemList:Array = new Array();
+		private var suggestionMenuItemList:Array = new Array();
+		private var _spellingEnabled:Boolean;
+		private var _contextMenu:ContextMenu;
+		private var mTextHighlighter:IHighlighter;
+		private var mWordProcessor:IWordProcessor;
+		private var mSpellEngine:SpellingService;
+		private var mParentTextField:TextField;
+		private var _ignoreWordFunctionProcessor:Function;
+		private var _misspelledToken:Token;
+		private var _misspelled:String;
+		public function SpellingContextMenu(textHighlighter:IHighlighter, wordProcessor:IWordProcessor, engine:SpellingService, actualParent:*, contextMenu:ContextMenu=null)
+		{
+			
+			if ( textHighlighter == null || wordProcessor == null ||  engine == null) throw new Error("illegal argument."); 
+			mTextHighlighter = textHighlighter;
+			mWordProcessor = wordProcessor;
+			mSpellEngine = engine;
+			mParentTextField = actualParent;
+			if (contextMenu != null) {
+				_contextMenu =contextMenu;
+			}else {
+				_contextMenu = new ContextMenu();
+			}
+			enableMenuItem.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT, handleEnableSpellCheck);
+			disableMenuItem.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT, handleDisableSpellCheck);
+			controlMenuItemList.push(enableMenuItem);
+			controlMenuItemList.push(disableMenuItem);
+			
+			_contextMenu.customItems.push(disableMenuItem);
+			_contextMenu.customItems.push(enableMenuItem);
+			spellingEnabled = true; //default value
+			//spellingEnabled= mTextHighlighter.spellingEnabled;
+			_contextMenu.addEventListener(ContextMenuEvent.MENU_SELECT, handleContextMenuSelect);
+			_ignoreWordFunctionProcessor=null;
+		}
+		
+		public function cleanUp():void
+		{
+			mTextHighlighter=null;
+			mWordProcessor=null;
+			spellingEnabled = false;
+			_ignoreWordFunctionProcessor=null;
+			
+			_contextMenu.removeEventListener(ContextMenuEvent.MENU_SELECT, handleContextMenuSelect);
+			
+			var removedNum:int = 0;
+			var count:uint = _contextMenu.customItems.length;
+			for (var j:uint=count; j>0; j--) {
+				if ( isWordItem(_contextMenu.customItems[j-1]) || isControlItem(_contextMenu.customItems[j-1]) ) {
+					_contextMenu.customItems.splice(j-1,1);
+					removedNum++
+				}
+			}
+			if ( removedNum != suggestionMenuItemList.length + controlMenuItemList.length ) {
+				trace("internal error");
+			}
+			
+			suggestionMenuItemList = null;
+			controlMenuItemList = null;
+		}
+		
+		public function get contextMenu():ContextMenu {
+			return this._contextMenu;
+		}
+
+		private function handleContextMenuSelect(event:ContextMenuEvent):void
+		{
+			/* Clear the context menu */
+			//spellingEnabled= mTextHighlighter.spellingEnabled;
+			//SpellUI.doSpelling1();
+			var removedNum:int = 0;
+			var count:uint = _contextMenu.customItems.length;
+			for (var j:uint=count; j>0; j--) {
+				if ( isWordItem(_contextMenu.customItems[j-1]) ) {
+					_contextMenu.customItems.splice(j-1,1);
+					removedNum++
+				}
+			}
+			if ( removedNum != suggestionMenuItemList.length ) {
+				trace("internal error");
+			}
+			
+			
+			suggestionMenuItemList = new Array();
+	
+			// localized entries
+			var entries:Object = SpellUI.getSpellingMenuEntries();
+			disableMenuItem.caption = entries.disable;
+			enableMenuItem.caption = entries.enable;				
+
+			if (spellingEnabled == true) {
+				_misspelledToken = mWordProcessor.getWordAtPoint(mParentTextField.mouseX, mParentTextField.mouseY);
+				if (_misspelledToken==null) return;
+				_misspelled = 	mParentTextField.text.substring(_misspelledToken.first, _misspelledToken.last);
+				if ((_misspelled==null) || (_misspelled == "")) return;
+				
+				if (mSpellEngine.checkWord(_misspelled)==true) return;				
+				
+				var suseAddToItem:ContextMenuItem = new ContextMenuItem(entries.add);
+				suseAddToItem.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT, handleAddToItemSelect);
+				suggestionMenuItemList.push(suseAddToItem);
+				_contextMenu.customItems.splice(0,0,suseAddToItem);	
+				//var result:Array = mWordProcessor.getSuggestionsAtPoint();
+				var resultVector:Vector.<String> = mSpellEngine.getSuggestions(_misspelled);
+				var result:Array = new Array();
+				if (resultVector) {
+					for each (var w:String in resultVector)
+					result.push(w);
+				}
+				if (result!=null) {
+					for (var i:int=result.length-1;i>=0;i-- ) {
+						var suseMenuItem:ContextMenuItem = new ContextMenuItem(result[i]);
+						suseMenuItem.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT, handleSuseItemSelect);
+						suggestionMenuItemList.push(suseMenuItem);
+						//_contextMenu.customItems.push(suseMenuItem);
+						_contextMenu.customItems.splice(0,0,suseMenuItem);
+					}
+				}
+			}
+			
+
+			
+		}
+		
+		public function setIgnoreWordCallback(func:Function ) :void {
+			if ( func != null )
+			_ignoreWordFunctionProcessor = func;
+		}
+		
+		private function handleAddToItemSelect(event:ContextMenuEvent):void
+		{
+			if ( _ignoreWordFunctionProcessor == null ) return;
+			
+			/*
+			var menuEntry:String = (event.currentTarget as ContextMenuItem).caption;
+			var start:uint = 5;
+			var end:uint = menuEntry.length - 15;
+			var word:String = menuEntry.substring(start, end);
+			*/
+			_ignoreWordFunctionProcessor(_misspelled);
+			//SpellUI.UITable[SpellUI.parentComp[mParentTextField]].doSpellingJob();//Reason bug#2713972 Fix Added Under
+			//now implicitly calling dospelling on all text areas
+			for each (var tempUIComponent:SpellUI in SpellUI.UITable)
+			{
+				tempUIComponent.doSpellingJob();
+			}
+		}
+
+		private function isWordItem(item:ContextMenuItem):Boolean {
+			
+			for ( var i:int=0; i<suggestionMenuItemList.length; ++i ) {
+				if ( suggestionMenuItemList[i] == item ) return true;
+			}
+			return false;
+		}
+		
+		private function isControlItem(item:ContextMenuItem):Boolean {
+			for (var i:int=0; i<controlMenuItemList.length; ++i) {
+				if ( controlMenuItemList[i] == item) return true;
+			}
+			return false;
+		}
+
+		private function handleSuseItemSelect(event:ContextMenuEvent):void
+		{
+			mWordProcessor.replaceText(_misspelledToken, (event.currentTarget as ContextMenuItem).caption );
+			SpellUI.UITable[SpellUI.parentComp[mParentTextField]].doSpellingJob();
+		}
+
+		
+		private function set spellingEnabled(value:Boolean) :void {
+			_spellingEnabled = value;
+			disableMenuItem.visible=spellingEnabled;
+			enableMenuItem.visible=!spellingEnabled;
+		}
+		private function get spellingEnabled():Boolean {
+			return this._spellingEnabled;
+		}
+		private function handleEnableSpellCheck(event:ContextMenuEvent):void
+		{
+			spellingEnabled= true;
+			//mTextHighlighter.spellingEnabled= spellingEnabled;
+			//SpellUI.doSpellingJob();
+			//dispatchEvent(new Event(Event.RENDER));
+			
+			SpellUI.UITable[SpellUI.parentComp[mParentTextField]].spellingEnabled = spellingEnabled;
+			SpellUI.UITable[SpellUI.parentComp[mParentTextField]].doSpellingJob();
+			//spellCheckRange(getValidFirstWordIndex(), getValidLastWordIndex());
+		}
+		private function handleDisableSpellCheck(event:ContextMenuEvent):void
+		{
+			spellingEnabled= false;
+			SpellUI.UITable[SpellUI.parentComp[mParentTextField]].spellingEnabled = spellingEnabled;
+			mTextHighlighter.clearSquiggles();
+		}
+				
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/a52655ac/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/ui/HaloHighlighter.as
----------------------------------------------------------------------
diff --git a/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/ui/HaloHighlighter.as b/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/ui/HaloHighlighter.as
new file mode 100644
index 0000000..91b0d5f
--- /dev/null
+++ b/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/ui/HaloHighlighter.as
@@ -0,0 +1,126 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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 com.adobe.linguistics.spelling.ui
+{
+	import __AS3__.vec.Vector;
+	
+	import com.adobe.linguistics.utils.Token;
+	
+	import flash.geom.Point;
+	import flash.text.TextField;
+	
+	import mx.core.IUITextField;
+	import com.adobe.linguistics.spelling.ui.IHighlighter;
+	import com.adobe.linguistics.spelling.ui.SpellingHighlighter;
+
+	/**
+	 * <p>This class facilitates drawing of squiggly lines below words for TextField class. TextField class is used to create display objects for text display 
+	 * and input for Halo TextArea and TextInput components. HaloHighlighter could therefore be used for drawing squiggly lines in these Halo components.</p>
+	 * 	
+	 * @playerversion Flash 10
+	 * @langversion 3.0
+	 */
+
+	public class HaloHighlighter implements IHighlighter
+	{
+		private var mTextField:TextField;
+		private var mHighlighter:SpellingHighlighter;
+		/*
+		* offset point:
+		*/
+		private var _offsetPoint:Point;
+
+		/**
+		 * The constructor for HaloHighlighter.
+		 * @param textField <code>TextField</code> in which to enable highlighting.		 
+		 * @playerversion Flash 10
+		 * @langversion 3.0
+		 */
+		public function HaloHighlighter( textField:TextField )
+		{
+			if (textField == null ) throw new Error("illegal argument."); 
+			mTextField = textField;
+			mHighlighter = null;
+			this._offsetPoint = new Point(0,0);
+		}
+		/**
+		 * Draw squiggly lines below a given token.
+		 * @param token <code>Token</code> information of the word to be highlighted.		 
+		 * @playerversion Flash 10
+		 * @langversion 3.0
+		 */
+		public function drawSquiggleAt(token:Token):void
+		{
+			squiggleWord(token);
+		}
+		
+		/**
+		 * Clear all squiggly lines in the TextField.		
+		 * @playerversion Flash 10
+		 * @langversion 3.0
+		 */
+		public function clearSquiggles():void
+		{
+			if (mHighlighter) {
+				mTextField.parent.removeChild(mHighlighter);
+				mHighlighter=null;
+			}
+			
+		}
+		
+		/**
+		 * Set offset point information for scrollable controls. This is used by the highlighter to move 
+		 * the squiggly lines as the text scrolls inside the control.	
+		 * @param op offset information as a <code>Point</code> instance.		 
+		 * @playerversion Flash 10
+		 * @langversion 3.0
+		 */		
+		public function set offsetPoint(op:Point):void{
+			_offsetPoint = op;
+		}
+		
+		/**
+		 * Get offset point information for scrollable controls. This is used by the highlighter to move 
+		 * the squiggly lines as the text scrolls inside the control.	
+		 * @param op offset information as a <code>Point</code> instance.		 
+		 * @playerversion Flash 10
+		 * @langversion 3.0
+		 */		
+		public function get offsetPoint():Point{
+			return _offsetPoint;
+		}
+		
+
+		private function squiggleWord(token:Token):void {
+						
+			if (!mHighlighter) {
+				mHighlighter= new SpellingHighlighter( mTextField as IUITextField);
+				mTextField.parent.addChild(mHighlighter);				
+			}
+						
+			mHighlighter.drawSquigglyLine(token.first, token.last);
+		
+		
+			//mTextField.parent.addChild(mHighlighter);	
+			mHighlighter.move(_offsetPoint.x, _offsetPoint.y);
+		}
+
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/a52655ac/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/ui/HaloWordProcessor.as
----------------------------------------------------------------------
diff --git a/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/ui/HaloWordProcessor.as b/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/ui/HaloWordProcessor.as
new file mode 100644
index 0000000..9c05b3b
--- /dev/null
+++ b/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/ui/HaloWordProcessor.as
@@ -0,0 +1,112 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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 com.adobe.linguistics.spelling.ui
+{
+	import com.adobe.linguistics.utils.ITokenizer;
+	import com.adobe.linguistics.utils.TextTokenizer;
+	import com.adobe.linguistics.utils.Token;
+	
+	import flash.text.TextField;
+	import flash.text.TextFormat;
+	
+	import mx.controls.TextArea;
+	import mx.controls.TextInput;
+	import com.adobe.linguistics.spelling.ui.IWordProcessor;
+
+
+	public class HaloWordProcessor implements IWordProcessor
+	{
+		private var mTextField:TextField;
+
+		public function HaloWordProcessor(textField:TextField)
+		{
+			if (textField == null ) throw new Error("illegal argument."); 
+			mTextField = textField;
+		}
+				
+		
+		public function replaceText(token:Token, replacement:String):void {
+			var startIndex:int = token.first;
+			var endIndex:int = token.last;
+			
+			if ( replacement == null ) return;
+			
+			if (mTextField.text.length<endIndex || startIndex<0) {
+				return;
+			}
+			
+			var _misspellStart:int = startIndex;
+			var _misspellEnd:int = endIndex;
+			// Try to preserve the format, this works if the whole misspelled word is the same format
+			var tf:TextFormat = mTextField.getTextFormat(_misspellStart, _misspellEnd);
+			mTextField.replaceText(_misspellStart, _misspellEnd, replacement);	
+			mTextField.setTextFormat(tf, _misspellStart, _misspellStart+replacement.length);
+			
+			var ta:TextArea = mTextField.parent as TextArea;
+			var ti:TextInput = mTextField.parent as TextInput;
+			
+			if (ta != null) {
+				ta.selectionBeginIndex = _misspellStart + replacement.length;
+				ta.selectionEndIndex = _misspellStart + replacement.length;
+			}
+			else if (ti != null) {
+				ti.selectionBeginIndex = _misspellStart + replacement.length;
+				ti.selectionEndIndex = _misspellStart + replacement.length;				
+			}
+			else {
+				// Do nothing if it's not a valid text component
+			}
+		}
+
+		
+		public function getWordAtPoint(x:uint, y:uint, externalTokenizer:ITokenizer=null):Token
+		{
+			var _token:Token = tryGetWordAtPoint(x,y, externalTokenizer);
+			return _token;
+		}
+		
+		private function tryGetWordAtPoint(x:uint, y:uint, externalTokenizer:ITokenizer=null):Token {
+			// TODO: use a better alternative than _misspellStart, end
+			var index:uint = mTextField.getCharIndexAtPoint(x + mTextField.scrollH, y);
+			if (index >= mTextField.text.length) return null;
+
+			var tmpToken:Token = new Token(index,index);
+			var tokenizer:ITokenizer;
+			if ( externalTokenizer == null ) {
+				tokenizer = new TextTokenizer(mTextField.text);	
+			}else {
+				tokenizer = externalTokenizer;
+			}
+			
+			var result:Token = new Token(0,0);
+			
+			var preToken:Token = tokenizer.getPreviousToken(tmpToken);
+			var nextToken:Token = tokenizer.getNextToken(tmpToken);
+			if ( preToken.last == nextToken.first ) {
+				result.first = preToken.first;
+				result.last = nextToken.last;
+				return result;		
+			}else {
+				return null;
+			}
+		}
+
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/a52655ac/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/ui/IHighlighter.as
----------------------------------------------------------------------
diff --git a/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/ui/IHighlighter.as b/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/ui/IHighlighter.as
new file mode 100644
index 0000000..3b5a309
--- /dev/null
+++ b/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/ui/IHighlighter.as
@@ -0,0 +1,67 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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 com.adobe.linguistics.spelling.ui
+{
+	import com.adobe.linguistics.utils.Token;
+	import __AS3__.vec.Vector;
+	import flash.geom.Point;
+	
+	/**
+	 * The <code>IHighlighter</code> Interface.
+	 * This interface defines default methods which will be used for highlighting text in UI components.
+	 *
+	 * @playerversion Flash 10
+	 * @langversion 3.0
+	 */
+	
+	public interface IHighlighter
+	{
+		/**
+		 * Draw squiggly lines below a given token.
+		 * @param token <code>Token</code> information of the word to be highlighted.		 
+		 * @playerversion Flash 10
+		 * @langversion 3.0
+		 */
+		function drawSquiggleAt(token:Token):void;
+		/**
+		 * Clear all squiggly lines in the UI.		
+		 * @playerversion Flash 10
+		 * @langversion 3.0
+		 */
+		function clearSquiggles():void;
+		/**
+		 * Set offset point information for scrollable controls. This is used by the highlighter to move 
+		 * the squiggly lines as the text scrolls inside the control.	
+		 * @param op offset information as a <code>Point</code> instance.		 
+		 * @playerversion Flash 10
+		 * @langversion 3.0
+		 */		
+		function set offsetPoint(op:Point):void;
+		/**
+		 * Get offset point information for scrollable controls. This is used by the highlighter to move 
+		 * the squiggly lines as the text scrolls inside the control.	
+		 * @param op offset information as a <code>Point</code> instance.		 
+		 * @playerversion Flash 10
+		 * @langversion 3.0
+		 */		
+		function get offsetPoint():Point;
+		
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/a52655ac/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/ui/IWordProcessor.as
----------------------------------------------------------------------
diff --git a/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/ui/IWordProcessor.as b/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/ui/IWordProcessor.as
new file mode 100644
index 0000000..8196691
--- /dev/null
+++ b/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/ui/IWordProcessor.as
@@ -0,0 +1,30 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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 com.adobe.linguistics.spelling.ui
+{
+	import com.adobe.linguistics.utils.ITokenizer;
+	import com.adobe.linguistics.utils.Token;
+	
+	public interface IWordProcessor
+	{
+		function getWordAtPoint(x:uint, y:uint, externalTokenizer:ITokenizer=null):Token;
+		function replaceText(token:Token, replacement:String):void;
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/a52655ac/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/ui/SparkHighlighter.as
----------------------------------------------------------------------
diff --git a/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/ui/SparkHighlighter.as b/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/ui/SparkHighlighter.as
new file mode 100644
index 0000000..ffe9653
--- /dev/null
+++ b/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/ui/SparkHighlighter.as
@@ -0,0 +1,229 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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 com.adobe.linguistics.spelling.ui
+{
+	import com.adobe.linguistics.utils.Token;
+	import com.adobe.linguistics.utils.TextTokenizer;
+	import flash.geom.Point;
+	
+	import flash.display.Shape;
+	import flash.geom.Rectangle;
+	import flash.text.engine.TextLine;
+	
+	import flashx.textLayout.compose.TextFlowLine;
+	import flashx.textLayout.edit.SelectionManager;
+	import flashx.textLayout.elements.TextFlow;
+	import flashx.textLayout.tlf_internal;
+	
+	import spark.components.RichEditableText;
+	import com.adobe.linguistics.spelling.ui.IHighlighter;
+
+	use namespace tlf_internal;	
+	
+	/**
+	 * <p>This class facilitates drawing of squiggly lines below words for RichEditableText class. RichEditableText is a low-level UIComponent for displaying, 
+	 * scrolling, selecting, and editing richly-formatted text. This class is used in the skins of the Spark versions of TextInput and TextArea. 
+	 * SparkHighlighter could therefore be used for drawing squiggly lines in these Spark components.</p>
+	 * 	
+	 * @playerversion Flash 10
+	 * @langversion 3.0
+	 */
+	public class SparkHighlighter implements IHighlighter
+	{
+		
+		private var mTextField:RichEditableText;
+		private var mHighlighter:Shape;
+		/*
+		* offset point:
+		*/
+		private var _offsetPoint:Point;
+
+		/**
+		 * The constructor for SparkHighlighter.
+		 * @param richEditableText <code>RichEditableText</code> in which to enable highlighting.		 
+		 * @playerversion Flash 10
+		 * @langversion 3.0
+		 */
+		public function SparkHighlighter( richEditableText:RichEditableText )
+		{
+			if (richEditableText == null ) throw new Error("illegal argument."); 
+			mTextField = richEditableText;
+			mHighlighter = null;
+			this._offsetPoint = new Point(0,0);
+		}
+
+		/**
+		 * Draw squiggly lines below a given token.
+		 * @param token <code>Token</code> information of the word to be highlighted.		 
+		 * @playerversion Flash 10
+		 * @langversion 3.0
+		 */
+		public function drawSquiggleAt(token:Token):void
+		{
+			squiggleWord(token);
+		}
+		
+		/**
+		 * Clear all squiggly lines in the component.		
+		 * @playerversion Flash 10
+		 * @langversion 3.0
+		 */
+		public function clearSquiggles():void
+		{
+			if (mHighlighter) {
+				mTextField.removeChild(mHighlighter);
+				mHighlighter=null;
+			}		
+		}
+		
+		/**
+		 * Set offset point information for scrollable controls. This is used by the highlighter to move 
+		 * the squiggly lines as the text scrolls inside the control.	
+		 * @param op offset information as a <code>Point</code> instance.		 
+		 * @playerversion Flash 10
+		 * @langversion 3.0
+		 */		
+		public function set offsetPoint(op:Point):void{
+			_offsetPoint = op;
+		}
+		
+		/**
+		 * Get offset point information for scrollable controls. This is used by the highlighter to move 
+		 * the squiggly lines as the text scrolls inside the control.	
+		 * @param op offset information as a <code>Point</code> instance.		 
+		 * @playerversion Flash 10
+		 * @langversion 3.0
+		 */		
+		public function get offsetPoint():Point{
+			return _offsetPoint;
+		}
+
+		
+
+		// TODO: refactor this code to share with halo components, and support words that cross lines
+		private function squiggleWord(token:Token):void {
+					
+			var ta:RichEditableText = mTextField;
+			if (!ta) return;		
+			
+			if (!mHighlighter) {
+				mHighlighter= new Shape();
+				mHighlighter.graphics.clear();
+				mTextField.addChild(mHighlighter);	
+			}
+				
+		        drawSquigglyLineForRange(token.first, token.last);
+			
+			// Just adjust the left padding, top padding is not an issue 
+			//var pleft:uint = mTextField.getStyle("paddingLeft");
+			//mHighlighter.x += pleft;		
+		}
+		
+		// Draw squiggly line
+		private function drawSquigglyLineForRange(start:Number, end:Number):void
+		{
+			// draw squiggly line
+			var tf:TextFlow = mTextField.textFlow;
+			var tflFirst:TextFlowLine = tf.flowComposer.findLineAtPosition(start);
+			var tflLast:TextFlowLine = tf.flowComposer.findLineAtPosition(end);
+			var tflIndexFirst:int = tf.flowComposer.findLineIndexAtPosition(start);
+			var tflIndexLast:int = tf.flowComposer.findLineIndexAtPosition(end);
+			
+			// Pointer
+			var tflIndex:int = tflIndexFirst;
+			var tfl:TextFlowLine = tflFirst;
+			
+			if (tflIndexFirst == tflIndexLast) {
+				// Draw one line
+				drawSquigglyLineAtIndex(tflIndexFirst, start - tflFirst.absoluteStart, end - tflFirst.absoluteStart);
+			} else {
+				// Multiple lines (very long word)
+				drawSquigglyLineAtIndex(tflIndexFirst, start - tflFirst.absoluteStart);
+				
+				tflIndex++;
+				while (tflIndex != tflIndexLast) {
+					drawSquigglyLineAtIndex(tflIndex);
+					tflIndex++;
+				}
+				
+				drawSquigglyLineAtIndex(tflIndexLast, 0, end - tflLast.absoluteStart);
+			}
+		}
+		
+		// Draw a squiggly line at specific line for specific index range
+		private function drawSquigglyLineAtIndex(lineIndex:Number, startIndex:Number=0, endIndex:Number=0x7FFFFFFF):void
+		{
+			var tf:TextFlow = mTextField.textFlow;
+			var tfl:TextFlowLine = tf.flowComposer.getLineAt(lineIndex);
+			var rectLine:Rectangle = tfl.getBounds();
+			if (endIndex == 0x7FFFFFFF) {
+				drawSquigglyLineAtPoint(rectLine.left, rectLine.bottom, rectLine.right - rectLine.left);
+			}
+			else {
+				// Force to have a valid TextLine
+				var tl:TextLine = tfl.getTextLine(true);
+				if(tl==null) return;
+				var atomStartIndex:int= tl.getAtomIndexAtCharIndex(startIndex+ tl.textBlockBeginIndex);//fix for diacritic characters bug#2854971(adding textBlockBeginIndex since getAtomIndexAtCharIndex is relative to beginning of block)
+				var atomEndIndex:int= tl.getAtomIndexAtCharIndex(endIndex+tl.textBlockBeginIndex);//fix for diacritic characters bug#2854971
+				// TODO: atom index and char index is not matching for some chars, use try/catch to avoid crash
+				try {
+					var rectFirst:Rectangle = tl.getAtomBounds(atomStartIndex);
+					var rectLast:Rectangle = tl.getAtomBounds(atomEndIndex);
+					drawSquigglyLineAtPoint(rectFirst.left + tfl.x, rectLine.bottom, rectLast.right - rectFirst.left);
+				} catch (err:Error)
+				{
+					trace(err);
+				}
+			}
+				
+		}
+		// Draw a squiggly from point x,y with given width, the line is drew in mHighlighter 
+		private function drawSquigglyLineAtPoint(x:Number, y:Number, width:Number):void
+		{
+			mHighlighter.graphics.lineStyle(1, 0xfa0707, .65);
+			mHighlighter.graphics.moveTo(x, y);
+			var upDirection:Boolean = false;
+			var offset:uint = 0;
+			var stepLength:uint = 2;
+			for ( var i:uint = 1; offset <= width; i++) {
+				offset = offset + stepLength;
+				if ( upDirection )
+					mHighlighter.graphics.lineTo(x+offset,y);
+				else
+					mHighlighter.graphics.lineTo(x+offset,y+stepLength);
+				upDirection = !upDirection;
+			}	
+		}
+		
+		private function getValidFirstWordIndex():int{
+			var index:int = SelectionManager.computeSelectionIndex(mTextField.textFlow, mTextField, mTextField, 0 + mTextField.horizontalScrollPosition, 0 + mTextField.verticalScrollPosition);
+			return index;
+
+			
+		}
+		
+		private function getValidLastWordIndex():int{
+			var index:int = SelectionManager.computeSelectionIndex(mTextField.textFlow, mTextField, mTextField, mTextField.width+mTextField.horizontalScrollPosition, mTextField.height+mTextField.verticalScrollPosition);
+			return index;
+
+		}
+
+	}
+}

http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/a52655ac/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/ui/SparkWordProcessor.as
----------------------------------------------------------------------
diff --git a/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/ui/SparkWordProcessor.as b/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/ui/SparkWordProcessor.as
new file mode 100644
index 0000000..3ae7a88
--- /dev/null
+++ b/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/ui/SparkWordProcessor.as
@@ -0,0 +1,133 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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 com.adobe.linguistics.spelling.ui
+{
+	import com.adobe.linguistics.utils.ITokenizer;
+	import com.adobe.linguistics.utils.TextTokenizer;
+	import com.adobe.linguistics.utils.Token;
+	
+	import flashx.textLayout.edit.SelectionManager;
+	import flashx.textLayout.tlf_internal;
+	
+	import spark.components.RichEditableText;
+	import com.adobe.linguistics.spelling.ui.IWordProcessor;
+	
+	use namespace tlf_internal;	
+	
+	public class SparkWordProcessor implements IWordProcessor
+	{
+		private var mTextField:RichEditableText;
+
+		public function SparkWordProcessor(textField:RichEditableText)
+		{
+			if (textField == null ) throw new Error("illegal argument."); 
+			mTextField = textField;
+		}
+				
+		
+		public function replaceText(token:Token, replacement:String):void {
+			var startIndex:int = token.first;
+			var endIndex:int = token.last;
+			
+			var ta:RichEditableText = mTextField;
+			var end:int = getValidLastWordIndex();
+			
+			if ( replacement == null ) return;
+			
+			if (mTextField.text.length<endIndex || startIndex<0) {
+				return;
+			}
+			
+			var _misspellStart:int = startIndex;
+			var _misspellEnd:int = endIndex;
+			
+			// Workaround for Spark: changes in inactive components will trigger strange behavior			
+			//var selectedElementRange:ElementRange = ElementRange.createElementRange(ta.textFlow, _misspellStart, _misspellEnd);
+			//var selectedCharacterFormat:ITextLayoutFormat = ta.textFlow.interactionManager.activePosition == ta.textFlow.interactionManager.anchorPosition ? ta.textFlow.interactionManager.getCommonCharacterFormat() : selectedElementRange.characterFormat;
+			//var selectedParagraphFormat:ITextLayoutFormat = selectedElementRange.paragraphFormat;
+			//var selectedContainerFormat:ITextLayoutFormat = selectedElementRange.containerFormat;
+			
+			
+			
+			//var selectedCharacterFormat:ITextLayoutFormat = ta.textFlow.interactionManager.getCommonCharacterFormat();
+			//var selectedContainerFormat:ITextLayoutFormat = ta.textFlow.interactionManager.getCommonContainerFormat();
+			//var selectedParagraphFormat:ITextLayoutFormat = ta.textFlow.interactionManager.getCommonParagraphFormat();
+			
+			/*var tem:EditManager = new EditManager();
+			ta.textFlow.interactionManager = tem;	*/
+			
+			
+			ta.setFocus();
+			//ta.text = ta.text.substr(0, _misspellStart) + replacement + ta.text.substr(_misspellEnd);
+			
+			//tem.applyFormat(selectedCharacterFormat,selectedParagraphFormat,selectedContainerFormat);
+			//ta.textFlow.flowComposer.updateAllControllers();
+			
+			ta.textFlow;
+			//ta.selectRange(_misspellStart + replacement.length, _misspellStart + replacement.length);
+			ta.selectRange(_misspellStart+1, _misspellEnd);
+			ta.insertText(replacement);
+			ta.selectRange(_misspellStart, _misspellStart+1);
+			ta.insertText("");
+			
+			//ta.textFlow.interactionManager.applyFormat(selectedCharacterFormat,null,null);
+			
+			// Workaround for unexpected jump
+			ta.scrollToRange(end, end);
+		}
+		
+		public function getWordAtPoint(x:uint, y:uint, externalTokenizer:ITokenizer=null):Token
+		{
+			// TODO: use a better alternative than _misspellStart, end
+			var ta:RichEditableText = mTextField;	
+			var index:int = SelectionManager.computeSelectionIndex(ta.textFlow, ta, ta, x, y);
+
+			if (index >= ta.text.length) return null;
+
+			var tmpToken:Token = new Token(index,index);
+			var tokenizer:ITokenizer;
+			if ( externalTokenizer == null ) {
+				tokenizer = new TextTokenizer(mTextField.text);	
+			}else {
+				tokenizer = externalTokenizer;
+			}
+			
+			var result:Token = new Token(0,0);
+			var preToken:Token = tokenizer.getPreviousToken(tmpToken);
+			var nextToken:Token = tokenizer.getNextToken(tmpToken);
+			if ( preToken.last == nextToken.first ) {
+				result.first = preToken.first;
+				result.last = nextToken.last;
+				return result;		
+			}else {
+				return null;
+			}
+				
+		}
+
+		// TODO: workaround for unexpected jump when word replaced, to be refactored for code sharing
+		private function getValidLastWordIndex():int{
+			var index:int = SelectionManager.computeSelectionIndex(mTextField.textFlow, mTextField, mTextField, mTextField.width+mTextField.horizontalScrollPosition, mTextField.height+mTextField.verticalScrollPosition);
+			return index;
+		}
+
+
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/a52655ac/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/ui/SpellingHighlighter.as
----------------------------------------------------------------------
diff --git a/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/ui/SpellingHighlighter.as b/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/ui/SpellingHighlighter.as
new file mode 100644
index 0000000..27cd2fb
--- /dev/null
+++ b/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/ui/SpellingHighlighter.as
@@ -0,0 +1,196 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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 com.adobe.linguistics.spelling.ui
+{
+	import flash.display.Shape;
+	import flash.geom.Point;
+	import flash.geom.Rectangle;
+	import flash.text.TextLineMetrics;
+	
+	import mx.core.IUITextField;
+	import mx.flash.UIMovieClip;
+
+
+	public class SpellingHighlighter extends UIMovieClip
+	{
+		/*
+		* offset point:
+		*/
+		private var _offsetPoint:Point;
+		
+		/*
+		* Target TextField.
+		*/
+		private var _textField:IUITextField;
+		private static var InvalidIndexValue:int = -2;
+		public function SpellingHighlighter(textField:IUITextField) {
+			super();
+			this._textField = textField;
+			this._offsetPoint = new Point(0,0);
+		}
+		
+		public function drawSquigglyLine(firstCharIndex:int, lastCharIndex:int ):void {
+			var validFirstCharIndex:int = getValidFirstCharIndex(firstCharIndex);
+			var validLastCharIndex:int = getValidLastCharIndex(lastCharIndex);
+			if ( validFirstCharIndex == InvalidIndexValue || validLastCharIndex == InvalidIndexValue ){
+				return;
+			}
+			/* draw squiggly line here. */
+			if ( validFirstCharIndex <= validLastCharIndex ) {
+				var firstLine:int = _textField.getLineIndexOfChar(validFirstCharIndex);
+				var lastLine:int = _textField.getLineIndexOfChar(validLastCharIndex);
+				//only one line case.
+				if(lastLine==firstLine)
+				{
+					drawSingleSquigglyLine(validFirstCharIndex, validLastCharIndex);
+					return;
+				}
+				//more than one line.
+				//first line
+				drawSingleSquigglyLine(validFirstCharIndex, _textField.getLineOffset(firstLine)+_textField.getLineLength(firstLine)-1);
+				//middle....
+				for(var i:int=firstLine+1;i<lastLine;i++)
+				{
+					drawSingleSquigglyLine(_textField.getLineOffset(i), _textField.getLineOffset(i)+_textField.getLineLength(i)-1);
+				}
+				//last lines.
+				drawSingleSquigglyLine(_textField.getLineOffset(lastLine), validLastCharIndex);
+			}
+		}
+		
+		public function drawSingleSquigglyLine(firstCharIndex:int, lastCharIndex:int ):void {
+			var firstLine:int = _textField.getLineIndexOfChar(firstCharIndex);
+			var lastLine:int = _textField.getLineIndexOfChar(lastCharIndex);
+			if ( firstLine != lastLine ) {
+				return;
+			}else {
+				var rect1:Rectangle = _textField.getCharBoundaries(firstCharIndex);
+				var rect2:Rectangle = _textField.getCharBoundaries(lastCharIndex);
+				var line:int;
+				while(rect1==null){ 
+					line=_textField.getLineIndexOfChar(firstCharIndex);
+					if( (firstCharIndex+1)>(_textField.getLineOffset(line)+_textField.getLineLength(line)-1)||(firstCharIndex+1)>lastCharIndex) return;// go till the last character
+					firstCharIndex++;
+					rect1= _textField.getCharBoundaries(firstCharIndex);										
+				}
+				
+				while(rect2==null){//this case appears for diacritic characters made using Decomposed chars
+					line=_textField.getLineIndexOfChar(lastCharIndex);
+					if( lastCharIndex-1 < (_textField.getLineOffset(line))||firstCharIndex>(lastCharIndex-1) ) return; //go till the first character
+					lastCharIndex--;// check for the previos character
+					rect2= _textField.getCharBoundaries(lastCharIndex);										
+				}
+				
+				
+				var x:Number = rect1.x+_offsetPoint.x - _textField.scrollH;
+				var y:Number = rect1.y + rect1.height + 2;
+				var width:Number = rect2.x+rect2.width-rect1.x;
+				
+				// Avoid drawing outside the textField
+				if (x<0) 
+				{
+					if (x+width > 0) {
+						width += x;
+						x = 0;
+					} 
+					else
+						return;
+				}
+				if (x+width > _textField.width) 
+				{
+					if (x < _textField.width) {
+						width = textField.width - x;
+					} 	
+					else
+						return;
+				}
+				
+				// The rectangle that bound the string you want
+				// actual work here.
+				var myShape:Shape = new Shape();
+				myShape.graphics.clear();
+				//myShape.graphics.beginFill(0x0099CC, .35); 
+				myShape.graphics.lineStyle(1, 0xfa0707, .65);
+				//myShape.graphics.moveTo(0,0);
+				myShape.graphics.moveTo(x, y);
+				var upDirection:Boolean = false;
+				var offset:uint = 0;
+				var stepLength:uint = 2;
+				for ( var i:uint = 1; offset <= width; i++) {
+					offset = offset + stepLength;
+					if ( upDirection )
+						myShape.graphics.lineTo(x+offset,y);
+					else
+						myShape.graphics.lineTo(x+offset,y+stepLength);
+					upDirection = !upDirection;
+				}
+				//myShape.graphics.endFill();
+				this.addChild(myShape);	
+			}
+		}
+		
+		private function getValidFirstCharIndex(firstCharIndex:int):int{
+			if(firstCharIndex<0 || firstCharIndex>_textField.text.length-1) 
+			{
+				return InvalidIndexValue;
+			}
+			var firstLine:Number = _textField.getLineIndexOfChar(firstCharIndex);
+			
+			if(firstLine<_textField.scrollV-1)
+			{
+				firstLine = _textField.scrollV-1;
+				return _textField.getLineOffset(firstLine);
+			}
+			return firstCharIndex;
+		}
+		
+		private function getValidLastCharIndex(lastCharIndex:int):int{
+			if(lastCharIndex<0 || lastCharIndex>_textField.text.length-1) 
+			{
+				return InvalidIndexValue;
+			}
+			var lastLine:Number = _textField.getLineIndexOfChar(lastCharIndex);
+			if(lastLine>_textField.bottomScrollV-1)
+			{
+				lastLine = _textField.bottomScrollV-1;
+				return _textField.getLineOffset(lastLine)+_textField.getLineLength(lastLine)-1;
+			}
+			return lastCharIndex;
+		}
+					
+		public function set textField(tf:IUITextField):void{
+			_textField = tf;
+		}
+		
+		public function get textField():IUITextField{
+			return _textField;
+		}
+		
+		public function set offsetPoint(op:Point):void{
+			_offsetPoint = op;
+		}
+		
+		public function get offsetPoint():Point{
+			return _offsetPoint;
+		}
+
+
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/a52655ac/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/ui/TLFHighlighter.as
----------------------------------------------------------------------
diff --git a/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/ui/TLFHighlighter.as b/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/ui/TLFHighlighter.as
new file mode 100644
index 0000000..92b7c71
--- /dev/null
+++ b/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/ui/TLFHighlighter.as
@@ -0,0 +1,252 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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 com.adobe.linguistics.spelling.ui
+{
+	import com.adobe.linguistics.utils.TextTokenizer;
+	import com.adobe.linguistics.utils.Token;
+	
+	import flash.display.Graphics;
+	import flash.display.Shape;
+	import flash.display.Sprite;
+	import flash.geom.Point;
+	import flash.geom.Rectangle;
+	import flash.text.engine.TextLine;
+	import flash.utils.Dictionary;
+	
+	import flashx.textLayout.compose.TextFlowLine;
+	import flashx.textLayout.container.ContainerController;
+	import flashx.textLayout.edit.SelectionManager;
+	import flashx.textLayout.elements.TextFlow;
+	import flashx.textLayout.tlf_internal;
+	import com.adobe.linguistics.spelling.ui.IHighlighter;
+
+
+	use namespace tlf_internal;	
+
+	/**
+	 * <p>This class facilitates drawing of squiggly lines below words for TLF TextFlow class.</p>
+	 * <p>The TextFlow class is responsible for managing all 
+	 * the text content of a story. In TextLayout, text is stored in a hierarchical tree of elements. TextFlow is the root object of the element tree. 
+	 * All elements on the tree derive from the base class, FlowElement. </p> 
+	 * TLFHighlighter could be used for drawing squiggly lines in any of the custom visual components(probably based on <code>Sprite</code>) which make use 
+	 * of TextFlow to display text.
+	 * 	
+	 * @playerversion Flash 10
+	 * @langversion 3.0
+	 */
+	public class TLFHighlighter implements IHighlighter
+	{
+		
+		private var mTextFlow:TextFlow;
+		private var mHighlighter:Dictionary;
+	
+		//private var mHighlighter:Shape;
+		private var ccindex:int;
+		private var cc:ContainerController;
+		/*
+		* offset point:
+		*/
+		private var _offsetPoint:Point;
+
+		/**
+		 * The constructor for TLFHighlighter.
+		 * @param textFlow <code>TextFlow</code> in which to enable highlighting.		 
+		 * @playerversion Flash 10
+		 * @langversion 3.0
+		 */
+		public function TLFHighlighter( textFlow:TextFlow )
+		{
+			if (textFlow == null ) throw new Error("illegal argument."); 
+			mTextFlow = textFlow;
+			//mHighlighter = null;
+			mHighlighter = new Dictionary(true);
+			this._offsetPoint = new Point(0,0);
+		}
+		/**
+		 * Draw squiggly lines below a given token.
+		 * @param token <code>Token</code> information of the word to be highlighted.		 
+		 * @playerversion Flash 10
+		 * @langversion 3.0
+		 */
+		public function drawSquiggleAt(token:Token):void
+		{
+			squiggleWord(token);
+		}
+		/**
+		 * Clear all squiggly lines in the component.		
+		 * @playerversion Flash 10
+		 * @langversion 3.0
+		 */
+		public function clearSquiggles():void
+		{
+			
+			for (var idx:int = 0; idx < mTextFlow.flowComposer.numControllers; idx++)
+			{	
+				var cctmp:ContainerController = mTextFlow.flowComposer.getControllerAt(idx);
+				if (!cctmp) return;	
+				if (mHighlighter[cctmp.container] != null) {
+					
+					//ToDO: This assumes single container for whole of mTextFlow. Need to implement for multiple container case.
+					cctmp.container.removeChild((mHighlighter[cctmp.container] as Shape));
+					
+					mHighlighter[cctmp.container] = null;
+				}	
+			}
+		}
+	
+		/**
+		 * Set offset point information for scrollable controls. This is used by the highlighter to move 
+		 * the squiggly lines as the text scrolls inside the control.	
+		 * @param op offset information as a <code>Point</code> instance.		 
+		 * @playerversion Flash 10
+		 * @langversion 3.0
+		 */		
+		public function set offsetPoint(op:Point):void{
+			_offsetPoint = op;
+		}
+		/**
+		 * Get offset point information for scrollable controls. This is used by the highlighter to move 
+		 * the squiggly lines as the text scrolls inside the control.	
+		 * @param op offset information as a <code>Point</code> instance.		 
+		 * @playerversion Flash 10
+		 * @langversion 3.0
+		 */	
+		public function get offsetPoint():Point{
+			return _offsetPoint;
+		}
+
+		
+
+		// TODO: refactor this code to share with halo components, and support words that cross lines
+		private function squiggleWord(token:Token):void {					
+			var ta:TextFlow = mTextFlow;
+			
+			if (!ta) return;		
+			ccindex = ta.flowComposer.findControllerIndexAtPosition(token.first);
+			
+			cc = ta.flowComposer.getControllerAt(ccindex);
+			if (!cc) return;	
+			if (mHighlighter[cc.container] == null ) {
+				mHighlighter[cc.container]= new Shape();
+				(mHighlighter[cc.container] as Shape).graphics.clear();
+				//ccindex = ta.flowComposer.findControllerIndexAtPosition(token.first);
+				
+				//var cc:ContainerController = ta.flowComposer.getControllerAt(ccindex);
+				//ToDO: This assumes single container for whole of mTextFlow. Need to implement for multiple container case.
+				cc.container.addChild((mHighlighter[cc.container] as Shape));				
+			}
+					
+		    drawSquigglyLineForRange(token.first, token.last);
+			
+			// Just adjust the left padding, top padding is not an issue 
+			//var pleft:uint = mTextFlow.getStyle("paddingLeft");
+			//mHighlighter.x += pleft;		
+		}
+		
+		// Draw squiggly line
+		private function drawSquigglyLineForRange(start:Number, end:Number):void
+		{
+			// draw squiggly line
+			var tf:TextFlow = mTextFlow;
+			var tflFirst:TextFlowLine = tf.flowComposer.findLineAtPosition(start);
+			var tflLast:TextFlowLine = tf.flowComposer.findLineAtPosition(end);
+			var tflIndexFirst:int = tf.flowComposer.findLineIndexAtPosition(start);
+			var tflIndexLast:int = tf.flowComposer.findLineIndexAtPosition(end);
+			
+			// Pointer
+			var tflIndex:int = tflIndexFirst;
+			var tfl:TextFlowLine = tflFirst;
+			
+			if (tflIndexFirst == tflIndexLast) {
+				// Draw one line
+				drawSquigglyLineAtIndex(tflIndexFirst, start - tflFirst.absoluteStart, end - tflFirst.absoluteStart);
+			} else {
+				// Multiple lines (very long word)
+				drawSquigglyLineAtIndex(tflIndexFirst, start - tflFirst.absoluteStart);
+				
+				tflIndex++;
+				while (tflIndex != tflIndexLast) {
+					drawSquigglyLineAtIndex(tflIndex);
+					tflIndex++;
+				}
+				
+				drawSquigglyLineAtIndex(tflIndexLast, 0, end - tflLast.absoluteStart);
+			}
+		}
+		
+		// Draw a squiggly line at specific line for specific index range
+		private function drawSquigglyLineAtIndex(lineIndex:Number, startIndex:Number=0, endIndex:Number=0x7FFFFFFF):void
+		{
+			var tf:TextFlow = mTextFlow;
+			var tfl:TextFlowLine = tf.flowComposer.getLineAt(lineIndex);
+			var rectLine:Rectangle = tfl.getBounds(); 
+			if (endIndex == 0x7FFFFFFF) {
+				drawSquigglyLineAtPoint(rectLine.left, rectLine.bottom, rectLine.right - rectLine.left, lineIndex);
+			}
+			else {
+				// Force to have a valid TextLine
+				var tl:TextLine = tfl.getTextLine(true);
+				if(tl==null)return;
+				var atomStartIndex:int= tl.getAtomIndexAtCharIndex(startIndex+ tl.textBlockBeginIndex);//fix for diacritic characters bug#2854971
+				var atomEndIndex:int= tl.getAtomIndexAtCharIndex(endIndex+ tl.textBlockBeginIndex);//fix for diacritic characters bug#2854971
+				// TODO: atom index and char index is not matching for some chars, use try/catch to avoid crash
+				try {
+					var rectFirst:Rectangle = tl.getAtomBounds(atomStartIndex);
+					var rectLast:Rectangle = tl.getAtomBounds(atomEndIndex);
+					drawSquigglyLineAtPoint(rectFirst.left + tfl.x, rectLine.bottom, rectLast.right - rectFirst.left, lineIndex);
+				} catch (err:Error)
+				{
+					trace(err);
+				}
+			}
+				
+		}
+		// Draw a squiggly from point x,y with given width, the line is drawn in mHighlighter 
+		private function drawSquigglyLineAtPoint(x:Number, y:Number, width:Number, lineIndex:Number):void
+		{
+			var tf:TextFlow = mTextFlow;
+			var tfl:TextFlowLine = tf.flowComposer.getLineAt(lineIndex);
+			var tl:TextLine = tfl.getTextLine(true);
+						
+			(mHighlighter[cc.container] as Shape).graphics.lineStyle(1, 0xfa0707, .65);
+			(mHighlighter[cc.container] as Shape).graphics.moveTo(x, y);
+			var upDirection:Boolean = false;
+			var offset:uint = 0;
+			var stepLength:uint = 2;
+			for ( var i:uint = 1; offset <= width; i++) {
+				offset = offset + stepLength;
+				if ( upDirection )
+					(mHighlighter[cc.container] as Shape).graphics.lineTo(x+offset,y);
+				else
+					(mHighlighter[cc.container] as Shape).graphics.lineTo(x+offset,y+stepLength);
+				upDirection = !upDirection;
+			}
+			
+			//tl.addChild(mHighlighter);
+						
+			//tf.flowComposer.updateToController(ccindex);
+
+		}
+		
+
+	}
+	
+}
+

http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/a52655ac/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/ui/TLFWordProcessor.as
----------------------------------------------------------------------
diff --git a/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/ui/TLFWordProcessor.as b/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/ui/TLFWordProcessor.as
new file mode 100644
index 0000000..749e218
--- /dev/null
+++ b/Squiggly/main/AdobeSpellingUI/src/com/adobe/linguistics/spelling/ui/TLFWordProcessor.as
@@ -0,0 +1,159 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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 com.adobe.linguistics.spelling.ui
+{
+	import com.adobe.linguistics.utils.ITokenizer;
+	import com.adobe.linguistics.utils.TextTokenizer;
+	import com.adobe.linguistics.utils.Token;
+	
+	import flashx.textLayout.edit.SelectionManager;
+	import flashx.textLayout.edit.EditManager;
+	import flashx.textLayout.tlf_internal;
+	import flashx.textLayout.elements.TextFlow;
+	
+	import flashx.textLayout.container.ContainerController;
+	import flashx.textLayout.elements.FlowLeafElement;
+	import flashx.textLayout.elements.ParagraphElement;
+	import com.adobe.linguistics.spelling.ui.IWordProcessor;
+	
+	use namespace tlf_internal;	
+	
+	public class TLFWordProcessor implements IWordProcessor
+	{
+		private var mTextFlow:TextFlow;
+		private var _containerController:ContainerController;
+
+		public function TLFWordProcessor(textFlow:TextFlow)
+		{
+			if (textFlow == null ) throw new Error("illegal argument."); 
+			mTextFlow = textFlow;
+		}
+				
+		
+		public function replaceText(token:Token, replacement:String):void {
+			var startIndex:int = token.first;
+			var endIndex:int = token.last;
+			
+			var ta:TextFlow = mTextFlow;
+			//var end:int = getValidLastWordIndex();
+			
+			if ( replacement == null ) return;
+			
+			/*if (mTextFlow.text.length<endIndex || startIndex<0) {
+				return;
+			}*/
+			
+			var _misspellStart:int = startIndex;
+			var _misspellEnd:int = endIndex;
+			
+			// Workaround for Spark: changes in inactive components will trigger strange behavior			
+			//var selectedElementRange:ElementRange = ElementRange.createElementRange(ta.textFlow, _misspellStart, _misspellEnd);
+			//var selectedCharacterFormat:ITextLayoutFormat = ta.textFlow.interactionManager.activePosition == ta.textFlow.interactionManager.anchorPosition ? ta.textFlow.interactionManager.getCommonCharacterFormat() : selectedElementRange.characterFormat;
+			//var selectedParagraphFormat:ITextLayoutFormat = selectedElementRange.paragraphFormat;
+			//var selectedContainerFormat:ITextLayoutFormat = selectedElementRange.containerFormat;
+			
+			
+			
+			//var selectedCharacterFormat:ITextLayoutFormat = ta.textFlow.interactionManager.getCommonCharacterFormat();
+			//var selectedContainerFormat:ITextLayoutFormat = ta.textFlow.interactionManager.getCommonContainerFormat();
+			//var selectedParagraphFormat:ITextLayoutFormat = ta.textFlow.interactionManager.getCommonParagraphFormat();
+			
+			var tem:EditManager = ta.interactionManager as EditManager;
+			
+			
+			
+			//ta.setFocus();
+			//ta.text = ta.text.substr(0, _misspellStart) + replacement + ta.text.substr(_misspellEnd);
+			
+			//tem.applyFormat(selectedCharacterFormat,selectedParagraphFormat,selectedContainerFormat);
+			//ta.textFlow.flowComposer.updateAllControllers();
+			
+			//ta.textFlow;
+			//ta.selectRange(_misspellStart + replacement.length, _misspellStart + replacement.length);
+			
+			
+			tem.selectRange(_misspellStart, _misspellEnd-1);
+			//tem.insertText(replacement);
+			//tem.selectRange(_misspellStart, _misspellStart+1);
+			//tem.insertText("");
+			tem.overwriteText(replacement);
+			
+			//ta.textFlow.interactionManager.applyFormat(selectedCharacterFormat,null,null);
+			
+			// Workaround for unexpected jump
+			//ta.scrollToRange(end, end);
+		}
+		
+		/**
+		 @private
+		 (This property is for Squiggly Developer use only.)
+		 */
+		public function set textFlowContainerController(value:ContainerController):void {
+			_containerController = value;
+		}
+		
+		public function getWordAtPoint(x:uint, y:uint, externalTokenizer:ITokenizer=null):Token
+		{
+			// TODO: use a better alternative than _misspellStart, end
+			var ta:TextFlow = mTextFlow;	
+									
+			var index:int = SelectionManager.computeSelectionIndex(ta, _containerController.container, _containerController.container, x, y);
+
+			if (index >= ta.textLength) return null;
+
+			var currentLeaf:FlowLeafElement = ta.findLeaf(index);
+			var currentParagraph:ParagraphElement = currentLeaf ? currentLeaf.getParagraph() : null;
+			
+			if (currentParagraph == null) return null;
+			var paraStart:uint = currentParagraph.getAbsoluteStart();
+				
+			//tokenizer = new TextTokenizer(currentParagraph.getText().substring());
+			
+			var tmpToken:Token = new Token(index - paraStart,index - paraStart);
+			var tokenizer:ITokenizer;
+			if ( externalTokenizer == null ) {
+				tokenizer = new TextTokenizer(currentParagraph.getText().substring());	
+			}else {
+				tokenizer = externalTokenizer;
+			}
+			
+			var result:Token = new Token(0,0);
+			var preToken:Token = tokenizer.getPreviousToken(tmpToken);
+			var nextToken:Token = tokenizer.getNextToken(tmpToken);
+			if ( preToken.last == nextToken.first ) {
+				result.first = preToken.first + paraStart;
+				result.last = nextToken.last + paraStart;
+				return result;		
+			}else {
+				return null;
+			}
+							
+		}
+
+		// TODO: workaround for unexpected jump when word replaced, to be refactored for code sharing
+		private function getValidLastWordIndex():int{
+			var index:int = 0;
+			//var index:int = SelectionManager.computeSelectionIndex(mTextFlow.textFlow, mTextFlow, mTextFlow, mTextFlow.width+mTextFlow.horizontalScrollPosition, mTextFlow.height+mTextFlow.verticalScrollPosition);
+			return index;
+		}
+
+
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/a52655ac/Squiggly/main/AdobeSpellingUIAPI/src/com/adobe/linguistics/spelling/ui/HaloHighlighter.as
----------------------------------------------------------------------
diff --git a/Squiggly/main/AdobeSpellingUIAPI/src/com/adobe/linguistics/spelling/ui/HaloHighlighter.as b/Squiggly/main/AdobeSpellingUIAPI/src/com/adobe/linguistics/spelling/ui/HaloHighlighter.as
new file mode 100644
index 0000000..28f16e6
--- /dev/null
+++ b/Squiggly/main/AdobeSpellingUIAPI/src/com/adobe/linguistics/spelling/ui/HaloHighlighter.as
@@ -0,0 +1,87 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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 com.adobe.linguistics.spelling.ui
+{
+	import __AS3__.vec.Vector;
+	
+	import com.adobe.linguistics.utils.Token;
+	
+	import flash.geom.Point;
+	import flash.text.TextField;
+	
+	import mx.core.IUITextField;
+
+
+	public class HaloHighlighter implements IHighlighter
+	{
+		private var mTextField:TextField;
+		private var mHighlighter:SpellingHighlighter;
+		/*
+		* offset point:
+		*/
+		private var _offsetPoint:Point;
+
+		public function HaloHighlighter( textField:TextField )
+		{
+			if (textField == null ) throw new Error("illegal argument."); 
+			mTextField = textField;
+			mHighlighter = null;
+			this._offsetPoint = new Point(0,0);
+		}
+		/*************************Public function************************************/
+		public function drawSquiggles(tokens:Vector.<Token>):void
+		{
+			spellCheckRange(tokens);
+		}
+		
+		public function clearSquiggles():void
+		{
+			if (mHighlighter) {
+				mTextField.parent.removeChild(mHighlighter);
+				mHighlighter=null;
+			}
+			
+		}
+
+		public function set offsetPoint(op:Point):void{
+			_offsetPoint = op;
+		}
+		
+		public function get offsetPoint():Point{
+			return _offsetPoint;
+		}
+
+
+		private function spellCheckRange(tokens:Vector.<Token>):void {
+			
+			mHighlighter= new SpellingHighlighter( mTextField as IUITextField);
+			
+			
+			for ( var i:int = 0; i< tokens.length; ++i ) {
+				var _token:Token = tokens[i];
+				mHighlighter.drawSquigglyLine(_token.first, _token.last);
+			}
+			
+			mTextField.parent.addChild(mHighlighter);	
+			mHighlighter.move(_offsetPoint.x, _offsetPoint.y);
+		}
+
+	}
+}
\ No newline at end of file