package gov.va.med.edp.widget { import gov.va.med.edp.util.KeyUtils; import flash.events.Event; import flash.events.FocusEvent; import flash.events.KeyboardEvent; import flash.net.SharedObject; import flash.ui.Keyboard; import mx.events.FlexEvent; import mx.collections.ArrayCollection; import mx.collections.ListCollectionView; import mx.controls.ComboBox; import mx.core.UIComponent; import mx.core.mx_internal; use namespace mx_internal; //-------------------------------------- // Events //-------------------------------------- /** * Dispatched when the filterFunction property changes. * * You can listen for this event and update the component * when the filterFunction property changes.

* * @eventType flash.events.Event */ [Event(name="filterFunctionChange", type="flash.events.Event")] /** * Dispatched when the typedText property changes. * * You can listen for this event and update the component * when the typedText property changes.

* * @eventType flash.events.Event */ [Event(name="typedTextChange", type="flash.events.Event")] //-------------------------------------- // Excluded APIs //-------------------------------------- [Exclude(name="editable", kind="property")] // added by jtorreno // created accessibility class [AccessibilityClass(implementation="gov.va.med.edp.widget.accessibility.AutoCompleteAccImpl")] /** * The AutoComplete control is an enhanced * TextInput control which pops up a list of suggestions * based on characters entered by the user. These suggestions * are to be provided by setting the dataProvider * property of the control. * @mxml * *

The <fc:AutoComplete> tag inherits all the tag attributes * of its superclass, and adds the following tag attributes:

* *
 *  <fc:AutoComplete
 *    Properties
 *    keepLocalHistory="false"
 *    lookAhead="false"
 *    typedText=""
 *    filterFunction="Internal filter function"
 *
 *    Events
 *    filterFunctionChange="No default"
 *    typedTextChange="No default"
 *  />
 *  
* * @includeExample ../../../../../../docs/com/adobe/flex/extras/controls/example/AutoCompleteCountriesData/AutoCompleteCountriesData.mxml * * @see mx.controls.ComboBox * */ public class AutoComplete extends ComboBox { // added by jtorreno 05.16.2008 mx_internal static var createAccessibilityImplementation:Function; //-------------------------------------------------------------------------- // // Constructor // //-------------------------------------------------------------------------- public function AutoComplete() { super(); // added by jtorreno tabChildren=true; //Make ComboBox look like a normal text field editable = true; if(keepLocalHistory) addEventListener("focusOut",focusOutHandler); setStyle("arrowButtonWidth",0); setStyle("fontWeight","normal"); setStyle("cornerRadius",0); setStyle("paddingLeft",0); setStyle("paddingRight",0); rowCount = 7; } //-------------------------------------------------------------------------- // // Variables // //-------------------------------------------------------------------------- /** * @private */ private var cursorPosition:Number=0; /** * @private */ private var prevIndex:Number = -1; /** * @private */ private var removeHighlight:Boolean = false; /** * @private */ private var showDropdown:Boolean=false; /** * @private */ private var showingDropdown:Boolean=false; /** * @private */ private var tempCollection:Object; /** * @private */ private var usingLocalHistory:Boolean=false; /** * @private */ private var dropdownClosed:Boolean=true; //-------------------------------------------------------------------------- // // Overridden Properties // //-------------------------------------------------------------------------- //---------------------------------- // editable //---------------------------------- /** * @private */ override public function set editable(value:Boolean):void { //This is done to prevent user from resetting the value to false super.editable = true; } /** * @private */ override public function set dataProvider(value:Object):void { super.dataProvider = value; if(!usingLocalHistory) tempCollection = value; } //---------------------------------- // labelField //---------------------------------- /** * @private */ override public function set labelField(value:String):void { super.labelField = value; invalidateProperties(); invalidateDisplayList(); } //-------------------------------------------------------------------------- // // Properties // //-------------------------------------------------------------------------- //---------------------------------- // filterFunction //---------------------------------- /** * @private * Storage for the filterFunction property. */ private var _filterFunction:Function = defaultFilterFunction; /** * @private */ private var filterFunctionChanged:Boolean = true; [Bindable("filterFunctionChange")] [Inspectable(category="General")] /** * A function that is used to select items that match the * function's criteria. * A filterFunction is expected to have the following signature: * *
f(item:~~, text:String):Boolean
* * where the return value is true if the specified item * should displayed as a suggestion. * Whenever there is a change in text in the AutoComplete control, this * filterFunction is run on each item in the dataProvider. * *

The default implementation for filterFunction works as follows:
* If "AB" has been typed, it will display all the items matching * "AB~~" (ABaa, ABcc, abAc etc.).

* *

An example usage of a customized filterFunction is when text typed * is a regular expression and we want to display all the * items which come in the set.

* * @example *
	 *  public function myFilterFunction(item:~~, text:String):Boolean
	 *  {
	 *     public var regExp:RegExp = new RegExp(text,"");
	 *     return regExp.test(item);
	 *  }
	 *  
* */ public function get filterFunction():Function { return _filterFunction; } /** * @private */ public function set filterFunction(value:Function):void { //An empty filterFunction is allowed but not a null filterFunction if(value!=null) { _filterFunction = value; filterFunctionChanged = true; invalidateProperties(); invalidateDisplayList(); dispatchEvent(new Event("filterFunctionChange")); } else _filterFunction = defaultFilterFunction; } //---------------------------------- // filterFunction //---------------------------------- /** * @private * Storage for the keepLocalHistory property. */ private var _keepLocalHistory:Boolean = false; /** * @private */ private var keepLocalHistoryChanged:Boolean = true; [Bindable("keepLocalHistoryChange")] [Inspectable(category="General")] /** * When true, this causes the control to keep track of the * entries that are typed into the control, and saves the * history as a local shared object. When true, the * completionFunction and dataProvider are ignored. * * @default "false" */ public function get keepLocalHistory():Boolean { return _keepLocalHistory; } /** * @private */ public function set keepLocalHistory(value:Boolean):void { _keepLocalHistory = value; } //---------------------------------- // lookAhead //---------------------------------- /** * @private * Storage for the lookAhead property. */ private var _lookAhead:Boolean=false; /** * @private */ private var lookAheadChanged:Boolean; [Bindable("lookAheadChange")] [Inspectable(category="Data")] /** * lookAhead decides whether to auto complete the text in the text field * with the first item in the drop down list or not. * * @default "false" */ public function get lookAhead():Boolean { return _lookAhead; } /** * @private */ public function set lookAhead(value:Boolean):void { _lookAhead = value; lookAheadChanged = true; } //---------------------------------- // typedText //---------------------------------- /** * @private * Storage for the typedText property. */ private var _typedText:String=""; /** * @private */ private var typedTextChanged:Boolean; [Bindable("typedTextChange")] [Inspectable(category="Data")] /** * A String to keep track of the text changed as * a result of user interaction. */ public function get typedText():String { return _typedText; } /** * @private */ public function set typedText(input:String):void { _typedText = input; typedTextChanged = true; invalidateProperties(); invalidateDisplayList(); dispatchEvent(new Event("typedTextChange")); } //-------------------------------------------------------------------------- // // Overridden methods // //-------------------------------------------------------------------------- override protected function createChildren():void { super.createChildren(); textInput.addEventListener(KeyboardEvent.KEY_DOWN, textInput_keyHandler); textInput.restrict = textInput.restrict + " "; // restrict space characters from being entered } /** * @private */ override protected function commitProperties():void { super.commitProperties(); if(!dropdown) selectedIndex=-1; if(dropdown) { if(typedTextChanged) { cursorPosition = textInput.selectionBeginIndex; updateDataProvider(); //In case there are no suggestions there is no need to show the dropdown if(collection.length==0 || typedText==""|| typedText==null) { dropdownClosed=true; showDropdown=false; //added by jtorreno //reset selected index if autocomplete field is empty selectedIndex = -1; } else { showDropdown = true; //selectedIndex = 0; // revised by jtorreno // removes default higlighted item on list selectedIndex = -1; } } } } /** * @private */ override protected function focusOutHandler(event:FocusEvent):void { super.focusOutHandler(event) if(keepLocalHistory && dataProvider.length==0) addToLocalHistory(); } /** * @private */ override public function getStyle(styleProp:String):* { if(styleProp != "openDuration") return super.getStyle(styleProp); else { if(dropdownClosed) return super.getStyle(styleProp); else return 0; } } /** * @private */ override protected function keyDownHandler(event:KeyboardEvent):void { super.keyDownHandler(event); if(!event.ctrlKey) { //An UP "keydown" event on the top-most item in the drop-down //or an ESCAPE "keydown" event should change the text in text // field to original text if(event.keyCode == Keyboard.UP && prevIndex==0) { textInput.text = _typedText; textInput.setSelection(textInput.text.length, textInput.text.length); selectedIndex = -1; } else if(event.keyCode==Keyboard.ESCAPE && showingDropdown) { textInput.text = _typedText; textInput.setSelection(textInput.text.length, textInput.text.length); showingDropdown = false; dropdownClosed=true; } else if (event.keyCode == Keyboard.SPACE && showingDropdown) { showingDropdown = false; dropdownClosed=true; } else if(event.keyCode == Keyboard.ENTER) { if(keepLocalHistory && dataProvider.length==0) addToLocalHistory(); } else if(lookAhead && KeyUtils.isRemoveKey(event)) removeHighlight = true; } else if(event.ctrlKey && event.keyCode == Keyboard.UP) dropdownClosed=true; prevIndex = selectedIndex; } /** * @private */ override protected function measure():void { super.measure(); measuredWidth = mx.core.UIComponent.DEFAULT_MEASURED_WIDTH; } /** * @private */ override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void { super.updateDisplayList(unscaledWidth, unscaledHeight); //An UP "keydown" event on the top-most item in the drop //down list otherwise changes the text in the text field to "" if(selectedIndex == -1) textInput.text = typedText; if(dropdown) { if(typedTextChanged) { //This is needed because a call to super.updateDisplayList() set the text // in the textInput to "" and thus the value //typed by the user losts if(lookAhead && showDropdown && typedText!="" && !removeHighlight) { var label:String = itemToLabel(collection[0]); var index:Number = label.toLowerCase().indexOf(_typedText.toLowerCase()); if(index==0) { textInput.text = _typedText+label.substr(_typedText.length); textInput.setSelection(textInput.text.length,_typedText.length); } else { textInput.text = _typedText; textInput.setSelection(cursorPosition, cursorPosition); removeHighlight = false; } } else { textInput.text = _typedText; textInput.setSelection(cursorPosition, cursorPosition); removeHighlight = false; } typedTextChanged= false; } else if(typedText) //Sets the selection when user navigates the suggestion list through //arrows keys. textInput.setSelection(_typedText.length,textInput.text.length); } if(showDropdown && !dropdown.visible) { //This is needed to control the open duration of the dropdown super.open(); showDropdown = false; showingDropdown = true; if(dropdownClosed) dropdownClosed=false; } } /** * @private */ override protected function textInput_changeHandler(event:Event):void { super.textInput_changeHandler(event); //Stores the text typed by the user in a variable typedText=text; if (text.charAt(text.length - 1) == ' ') { typedText = text.slice(0, text.length - 1); } } protected function textInput_keyHandler(event:KeyboardEvent):void { if (event.keyCode == Keyboard.SPACE && showingDropdown && selectedIndex != -1 && !event.ctrlKey) { // event.preventDefault(); dispatchEvent(new FlexEvent(FlexEvent.ENTER)); } } //-------------------------------------------------------------------------- // // Methods // //-------------------------------------------------------------------------- /** * @private * If keepLocalHistory is enabled, stores the text typed * by the user in the local history on the client machine */ private function addToLocalHistory():void { if (id != null && id != "" && text != null && text != "") { var so:SharedObject = SharedObject.getLocal("AutoCompleteData"); var savedData : Array = so.data.suggestions; //No shared object has been created so far if (savedData == null) savedData = new Array(); var i:Number=0; var flag:Boolean=false; //Check if this entry is there in the previously saved shared object data for(i=0;i