Spell checking in Flex with Squiggly - Part 2

Posted on May 05, 2010

In part 1 of my Squiggly for Flex tutorial, we discussed how easy it is to use the check-as-you-type spell checking for any TextInput or TextArea. This works great for most scenarios. However, sometimes you're requirements may ask for more customized spell-checking behavior. For example, perhaps you need to persist user custom dictionary items to a database or you need to build a custom spelling suggestion UI - these were just a couple of the requirements I faced. Thankfully, Squiggly provides most of the tools you will need to get these tasks done...with some caveats.

Customizing the Spell Checker
Our last tutorial focused almost completely on the SpellUI class provided with Squiggly. This time, we will work through using the other three classes that are included: SpellChecker, HunSpellingDictionary and UserDictionary. You would need to utilize these classes primarily when your application requires spellchecking functionality that is outside the SpellUI class' built in functionality discussed in Part 1.

My application required several customized spell-checking features in order to build a spelling wizard similar to MS Word's. These features include the ability to persist user-defined dictionary items in the database (SpellUI simply puts these in a shared object) and the ability to get a list of misspelled words from any UI element (SpellUI simply underlines them in red). I can't share the entire code of my proof-of-concept for the spelling wizard but I will show how I used Squiggly to get some of the basis of the wizard built into my code.

The ITextWithSpell Interface
In order to get access to any of the spell checking functionality within any given text element (TextInput or TextArea), I created an interface to enforce the methods I would require. EnableSpell and DisableSpell were discussed in Part 1 and enable and disable SpellUI functionality. The getMispelledWords() method will utilize SpellChecker class to return an array containing every misspelled word in the given text input. The replaceWord() method will simply replace the first instance of a word (its assumed this would be a misspelling) with another word (its assumed this would be the correct spelling). Finally, the addToDictionary() method will use the UserDictionary class to add the provided word to a user-defined dictionary. Below is the full code for the ITextWithSpell interface:

package com.views
{
   import flash.events.Event;

   public interface ITextWithSpell
   {
      function enableSpell(e:Event):void
      
      function disableSpell(e:Event):void
      
      function getMispelledWords():Array
         
      function replaceWord(word:String,wordToReplace:String):void
      
      function addToDictionary(word:String):void
   }
}

One of the things that this interface allows me to do, and which is completely necessary for a spelling wizard that spans multiple text inputs within an application, is to get an array of all text elements within an application that implement this interface. In doing so, you can then work through each item using the getMispelledWords() method to display any misspellings to the user. As I mentioned earlier, this is still a work in progress, but here is the method I use to get all items implementing the above interface (you will notice that it is recursive):

private function getAllSpellcheck(d:Container):Array {
   var resultsArr:Array = new Array();
   var children:Array = d.getChildren();
   
   for each (var child:UIComponent in children) {
      if (child is Container) {
         resultsArr = resultsArr.concat(getAllSpellcheck(child as Container));
      }
      else if (child is com.views.ITextWithSpell) {
         resultsArr.push(child);
      }
   }
   
   return resultsArr;
}

Adding Custom Spell Checking Functionality to a TextInput
Now that we have defined our methods using the interface, the next step is to actually implement those in a TextInput and TextArea. With the recent update to Squiggly some of the way you implement the SpellChecker class changed. Firstly, the dictionary class was changed to HunSpellDictionary. The HunSpellDictionary loads the dictionary file you want Squiggly to use to perform its spell check (in the code below, I use the US English dictionary provided with the Squiggly download files - just copy the dictionary folder into your source). Another change in the newer release was that you needed to add an event listener to listen for when Squiggly has finished loading the dictionary element before you can add a custom user dictionary.

Below is the beginning code for a text input with the custom spelling functions added that adds the constructor to load the dictionary file and a function to add our custom dictionary object when the dictionary file has complete loading. I have also left in the enableSpell and disableSpell functions I discussed in part 1. Note that using this code will give you an error at the moment because we haven't finished implementing all the methods required by the interface yet (we'll define those later):

package com.views
{
   import com.adobe.linguistics.spelling.*;
   import com.vo.MispellingVO;
   
   import flash.events.Event;
   import flash.net.URLRequest;
   
   import spark.components.TextInput;
   
   public class TextInputWithSpell extends TextInput implements ITextWithSpell
   {
      private var spellingDictionary:HunSpellDictionary = new HunSpellDictionary();
      private var spellchecker:SpellChecker;
      private var userDictionary:UserDictionary = new UserDictionary();
      
      public function TextInputWithSpell()
      {
         super();
         
         spellingDictionary.addEventListener(Event.COMPLETE, handleLoadComplete);
         spellingDictionary.load("dictionaries/en_US/en_US.aff","dictionaries/en_US/en_US.dic");
         
         addEventListener("focusIn",enableSpell);
         addEventListener("focusOut",disableSpell);
      }
      
      private function handleLoadComplete(e:Event):void
      {
         spellchecker = new SpellChecker(spellingDictionary);
         spellchecker.addUserDictionary(userDictionary);
      }
      
      public function enableSpell(e:Event):void {
         SpellUI.enableSpelling(this,"en_US");
      }
      
      public function disableSpell(e:Event):void {
         try {
            SpellUI.disableSpelling(this);
         }
         catch (error:Error) {
            // do nothing. sometimes the disable seemed to occur before the enable if you clicked too quickly
         }
      }
   }
}

Getting All Misspelled Words
I will admit that I thought this type of functionality would be something built into Squiggly's SpellChecker class along the lines of having the ability to pass it a string and it could return all the misspelled words within that string. While that function does not exist by default, it's pretty simple to create using Squiggly even if it seems slightly inefficient. As you can see in the method below from my TextInputWithSpell class, I simply loop through an array of words within the TextInput and use the SpellChecker class' checkWord() function to see if it is misspelled. If the word is misspelled, I create an instance of a MispellingVO value object I created into which I pass the word as well as the suggestions Squiggly has for the correct spelling (these are easily available via the getSuggestions() method in the SpellChecker class).

public function getMispelledWords():Array {
   var contentArr:Array = this.text.split(" ");
   var results:Array = new Array();
   var checkSpelling:Boolean;
   
   for (var i:String in contentArr) {
      checkSpelling = spellchecker.checkWord(contentArr[i]);
      if (checkSpelling == false) {
         results.push(new MispellingVO(contentArr[i],i as Number,spellchecker.getSuggestions(contentArr[i])));
      }
   }
   
   return results;
}

The replaceWord() function is designed to allow you to replace an instance of a misspelling with the corrected spelling. This method really has nothing to do with Squiggly necessarily but would be necessary if you were making a custom spell checker. It simply uses the built in replace() method in the String object. It is not fully fleshed out however since that method replaces the first instance of the match which would create an issue in the unlikely but plausible scenario that you have two instances of a misspelled word but only wanted to replace the second instance.

public function replaceWord(word:String,wordToReplace:String):void {
   this.text = this.text.replace(word,wordToReplace);
}

Custom User-Defined Dictionaries
Finally, the addToDictionary() method which is also not completely implemented. Adding a word to the userDictionary is as easy as calling the addWord() method as seen below. Where this is lacking however is that once you take responsibility for the user-defined dictionary items away from SpellUI, they are not persisted in any way for you (remember, SpellUI creates a shared object). In this case, we'd probably want to ensure that user defined items are persisted to the database perhaps by responding to some event and, in addition, we'd need to load these words into our UserDictionary object when its instantiated.

public function addToDictionary(word:String):void {
   userDictionary.addWord(word);
}

Going Forward & Some Issues
As I stated earlier, this was all part of a proof-of-concept and not a fully complete application so as noted some methods are not completely implemented while others could use some refinement. Nonetheless, Squiggly does offer a lot in terms of useful functionality if you need to go beyond the built-in spell-checking behavior is SpellUI. Nonetheless, there were some issues I ran into that I feel should be addressed in a future release.

Firstly, the user dictionary between spellUI and UserDictionary are not shareable. By this I mean that items added to SpellingDictionary still get underlined in SpellUI and items added via SpellUI can't be easily accessed to synchronize them with your SpellingDictionary. What this means is that if you want to use the highlighting of misspelled words built-in to SpellUI, you cannot do so with a custom UserDictionary (i.e. you'd have to build your own highlighter).

Secondly, as noted earlier, I think it would be useful if SpellChecker could return you an array of misspelled words for a given string as this seems to me a key requirement for any custom spell checking functionality.

All in all, however, I am quite pleased with the progress of the Squiggly project and hope that Adobe continues to devote effort into moving it forward.

Comments

Kyle Dodge Nice write up Brian. Any plans on putting up your sample swf to play with on the site?

Posted By Kyle Dodge / Posted on 05/05/2010 at 11:03 AM


Brian Rinaldi @Kyle - thanks! I wish I could share but all the custom functionality is wrapped up in the spell check wizard PoC that my boss has asked me not to share. My goal here was to share as much as I could without crossing his request not to share the wizard at the moment.

Posted By Brian Rinaldi / Posted on 05/05/2010 at 12:49 PM


Tahir Alvi very nice work

Posted By Tahir Alvi / Posted on 10/06/2010 at 5:40 AM


Write your comment



(it will not be displayed)





About

My name is Brian Rinaldi and I am the Web Community Manager for Flash Platform at Adobe. I am a regular blogger, speaker and author. I also founded RIA Unleashed conference in Boston. The views expressed on this site are my own & not those of my employer.