source: EDIS/trunk/java/tracking-ui-core/src/main/flex/gov/va/med/edp/widget/AutoComplete.as@ 1227

Last change on this file since 1227 was 1227, checked in by George Lilly, 13 years ago

initial load of EDIS 1.0

File size: 18.4 KB
Line 
1package gov.va.med.edp.widget
2{
3
4
5import gov.va.med.edp.util.KeyUtils;
6import flash.events.Event;
7import flash.events.FocusEvent;
8import flash.events.KeyboardEvent;
9import flash.net.SharedObject;
10import flash.ui.Keyboard;
11
12import mx.events.FlexEvent;
13import mx.collections.ArrayCollection;
14import mx.collections.ListCollectionView;
15import mx.controls.ComboBox;
16import mx.core.UIComponent;
17import mx.core.mx_internal;
18
19
20use namespace mx_internal;
21
22//--------------------------------------
23// Events
24//--------------------------------------
25
26/**
27 * Dispatched when the <code>filterFunction</code> property changes.
28 *
29 * You can listen for this event and update the component
30 * when the <code>filterFunction</code> property changes.</p>
31 *
32 * @eventType flash.events.Event
33 */
34[Event(name="filterFunctionChange", type="flash.events.Event")]
35
36/**
37 * Dispatched when the <code>typedText</code> property changes.
38 *
39 * You can listen for this event and update the component
40 * when the <code>typedText</code> property changes.</p>
41 *
42 * @eventType flash.events.Event
43 */
44[Event(name="typedTextChange", type="flash.events.Event")]
45
46//--------------------------------------
47// Excluded APIs
48//--------------------------------------
49
50[Exclude(name="editable", kind="property")]
51
52// added by jtorreno
53// created accessibility class
54[AccessibilityClass(implementation="gov.va.med.edp.widget.accessibility.AutoCompleteAccImpl")]
55
56/**
57 * The AutoComplete control is an enhanced
58 * TextInput control which pops up a list of suggestions
59 * based on characters entered by the user. These suggestions
60 * are to be provided by setting the <code>dataProvider
61 * </code> property of the control.
62 * @mxml
63 *
64 * <p>The <code>&lt;fc:AutoComplete&gt;</code> tag inherits all the tag attributes
65 * of its superclass, and adds the following tag attributes:</p>
66 *
67 * <pre>
68 * &lt;fc:AutoComplete
69 * <b>Properties</b>
70 * keepLocalHistory="false"
71 * lookAhead="false"
72 * typedText=""
73 * filterFunction="<i>Internal filter function</i>"
74 *
75 * <b>Events</b>
76 * filterFunctionChange="<i>No default</i>"
77 * typedTextChange="<i>No default</i>"
78 * /&gt;
79 * </pre>
80 *
81 * @includeExample ../../../../../../docs/com/adobe/flex/extras/controls/example/AutoCompleteCountriesData/AutoCompleteCountriesData.mxml
82 *
83 * @see mx.controls.ComboBox
84 *
85 */
86public class AutoComplete extends ComboBox
87{
88
89 // added by jtorreno 05.16.2008
90 mx_internal static var createAccessibilityImplementation:Function;
91
92 //--------------------------------------------------------------------------
93 //
94 // Constructor
95 //
96 //--------------------------------------------------------------------------
97 public function AutoComplete()
98 {
99 super();
100 // added by jtorreno
101 tabChildren=true;
102
103 //Make ComboBox look like a normal text field
104 editable = true;
105
106 if(keepLocalHistory)
107 addEventListener("focusOut",focusOutHandler);
108
109 setStyle("arrowButtonWidth",0);
110 setStyle("fontWeight","normal");
111 setStyle("cornerRadius",0);
112 setStyle("paddingLeft",0);
113 setStyle("paddingRight",0);
114 rowCount = 7;
115 }
116
117 //--------------------------------------------------------------------------
118 //
119 // Variables
120 //
121 //--------------------------------------------------------------------------
122
123 /**
124 * @private
125 */
126 private var cursorPosition:Number=0;
127
128 /**
129 * @private
130 */
131 private var prevIndex:Number = -1;
132
133 /**
134 * @private
135 */
136 private var removeHighlight:Boolean = false;
137
138 /**
139 * @private
140 */
141 private var showDropdown:Boolean=false;
142
143 /**
144 * @private
145 */
146 private var showingDropdown:Boolean=false;
147
148 /**
149 * @private
150 */
151 private var tempCollection:Object;
152
153 /**
154 * @private
155 */
156 private var usingLocalHistory:Boolean=false;
157
158 /**
159 * @private
160 */
161 private var dropdownClosed:Boolean=true;
162
163 //--------------------------------------------------------------------------
164 //
165 // Overridden Properties
166 //
167 //--------------------------------------------------------------------------
168
169 //----------------------------------
170 // editable
171 //----------------------------------
172 /**
173 * @private
174 */
175 override public function set editable(value:Boolean):void
176 {
177 //This is done to prevent user from resetting the value to false
178 super.editable = true;
179 }
180
181 /**
182 * @private
183 */
184 override public function set dataProvider(value:Object):void
185 {
186 super.dataProvider = value;
187 if(!usingLocalHistory)
188 tempCollection = value;
189 }
190
191 //----------------------------------
192 // labelField
193 //----------------------------------
194 /**
195 * @private
196 */
197 override public function set labelField(value:String):void
198 {
199 super.labelField = value;
200
201 invalidateProperties();
202 invalidateDisplayList();
203 }
204
205
206 //--------------------------------------------------------------------------
207 //
208 // Properties
209 //
210 //--------------------------------------------------------------------------
211
212
213 //----------------------------------
214 // filterFunction
215 //----------------------------------
216
217 /**
218 * @private
219 * Storage for the filterFunction property.
220 */
221 private var _filterFunction:Function = defaultFilterFunction;
222
223 /**
224 * @private
225 */
226 private var filterFunctionChanged:Boolean = true;
227
228 [Bindable("filterFunctionChange")]
229 [Inspectable(category="General")]
230
231 /**
232 * A function that is used to select items that match the
233 * function's criteria.
234 * A filterFunction is expected to have the following signature:
235 *
236 * <pre>f(item:~~, text:String):Boolean</pre>
237 *
238 * where the return value is <code>true</code> if the specified item
239 * should displayed as a suggestion.
240 * Whenever there is a change in text in the AutoComplete control, this
241 * filterFunction is run on each item in the <code>dataProvider</code>.
242 *
243 * <p>The default implementation for filterFunction works as follows:<br>
244 * If "AB" has been typed, it will display all the items matching
245 * "AB~~" (ABaa, ABcc, abAc etc.).</p>
246 *
247 * <p>An example usage of a customized filterFunction is when text typed
248 * is a regular expression and we want to display all the
249 * items which come in the set.</p>
250 *
251 * @example
252 * <pre>
253 * public function myFilterFunction(item:~~, text:String):Boolean
254 * {
255 * public var regExp:RegExp = new RegExp(text,"");
256 * return regExp.test(item);
257 * }
258 * </pre>
259 *
260 */
261 public function get filterFunction():Function
262 {
263 return _filterFunction;
264 }
265
266 /**
267 * @private
268 */
269 public function set filterFunction(value:Function):void
270 {
271 //An empty filterFunction is allowed but not a null filterFunction
272 if(value!=null)
273 {
274 _filterFunction = value;
275 filterFunctionChanged = true;
276
277 invalidateProperties();
278 invalidateDisplayList();
279
280 dispatchEvent(new Event("filterFunctionChange"));
281 }
282 else
283 _filterFunction = defaultFilterFunction;
284 }
285
286 //----------------------------------
287 // filterFunction
288 //----------------------------------
289
290 /**
291 * @private
292 * Storage for the keepLocalHistory property.
293 */
294 private var _keepLocalHistory:Boolean = false;
295
296 /**
297 * @private
298 */
299 private var keepLocalHistoryChanged:Boolean = true;
300
301 [Bindable("keepLocalHistoryChange")]
302 [Inspectable(category="General")]
303
304 /**
305 * When true, this causes the control to keep track of the
306 * entries that are typed into the control, and saves the
307 * history as a local shared object. When true, the
308 * completionFunction and dataProvider are ignored.
309 *
310 * @default "false"
311 */
312 public function get keepLocalHistory():Boolean
313 {
314 return _keepLocalHistory;
315 }
316
317 /**
318 * @private
319 */
320 public function set keepLocalHistory(value:Boolean):void
321 {
322 _keepLocalHistory = value;
323 }
324
325 //----------------------------------
326 // lookAhead
327 //----------------------------------
328
329 /**
330 * @private
331 * Storage for the lookAhead property.
332 */
333 private var _lookAhead:Boolean=false;
334
335 /**
336 * @private
337 */
338 private var lookAheadChanged:Boolean;
339
340 [Bindable("lookAheadChange")]
341 [Inspectable(category="Data")]
342
343 /**
344 * lookAhead decides whether to auto complete the text in the text field
345 * with the first item in the drop down list or not.
346 *
347 * @default "false"
348 */
349 public function get lookAhead():Boolean
350 {
351 return _lookAhead;
352 }
353
354 /**
355 * @private
356 */
357 public function set lookAhead(value:Boolean):void
358 {
359 _lookAhead = value;
360 lookAheadChanged = true;
361 }
362
363 //----------------------------------
364 // typedText
365 //----------------------------------
366
367 /**
368 * @private
369 * Storage for the typedText property.
370 */
371 private var _typedText:String="";
372 /**
373 * @private
374 */
375 private var typedTextChanged:Boolean;
376
377 [Bindable("typedTextChange")]
378 [Inspectable(category="Data")]
379
380 /**
381 * A String to keep track of the text changed as
382 * a result of user interaction.
383 */
384 public function get typedText():String
385 {
386 return _typedText;
387 }
388
389 /**
390 * @private
391 */
392 public function set typedText(input:String):void
393 {
394 _typedText = input;
395 typedTextChanged = true;
396
397 invalidateProperties();
398 invalidateDisplayList();
399 dispatchEvent(new Event("typedTextChange"));
400 }
401
402 //--------------------------------------------------------------------------
403 //
404 // Overridden methods
405 //
406 //--------------------------------------------------------------------------
407
408 override protected function createChildren():void
409 {
410 super.createChildren();
411
412 textInput.addEventListener(KeyboardEvent.KEY_DOWN, textInput_keyHandler);
413 textInput.restrict = textInput.restrict + " "; // restrict space characters from being entered
414 }
415
416 /**
417 * @private
418 */
419 override protected function commitProperties():void
420 {
421 super.commitProperties();
422
423 if(!dropdown)
424 selectedIndex=-1;
425
426 if(dropdown)
427 {
428
429 if(typedTextChanged)
430 {
431 cursorPosition = textInput.selectionBeginIndex;
432
433 updateDataProvider();
434
435 //In case there are no suggestions there is no need to show the dropdown
436 if(collection.length==0 || typedText==""|| typedText==null)
437 {
438 dropdownClosed=true;
439 showDropdown=false;
440 //added by jtorreno
441 //reset selected index if autocomplete field is empty
442 selectedIndex = -1;
443 }
444 else
445 {
446 showDropdown = true;
447 //selectedIndex = 0;
448 // revised by jtorreno
449 // removes default higlighted item on list
450 selectedIndex = -1;
451
452 }
453 }
454 }
455 }
456
457 /**
458 * @private
459 */
460 override protected function focusOutHandler(event:FocusEvent):void
461 {
462 super.focusOutHandler(event)
463 if(keepLocalHistory && dataProvider.length==0)
464 addToLocalHistory();
465 }
466 /**
467 * @private
468 */
469 override public function getStyle(styleProp:String):*
470 {
471 if(styleProp != "openDuration")
472 return super.getStyle(styleProp);
473 else
474 {
475 if(dropdownClosed)
476 return super.getStyle(styleProp);
477 else
478 return 0;
479 }
480 }
481 /**
482 * @private
483 */
484 override protected function keyDownHandler(event:KeyboardEvent):void
485 {
486 super.keyDownHandler(event);
487
488 if(!event.ctrlKey)
489 {
490 //An UP "keydown" event on the top-most item in the drop-down
491 //or an ESCAPE "keydown" event should change the text in text
492 // field to original text
493 if(event.keyCode == Keyboard.UP && prevIndex==0)
494 {
495 textInput.text = _typedText;
496 textInput.setSelection(textInput.text.length, textInput.text.length);
497 selectedIndex = -1;
498 }
499 else if(event.keyCode==Keyboard.ESCAPE && showingDropdown)
500 {
501 textInput.text = _typedText;
502 textInput.setSelection(textInput.text.length, textInput.text.length);
503 showingDropdown = false;
504 dropdownClosed=true;
505 }
506 else if (event.keyCode == Keyboard.SPACE && showingDropdown)
507 {
508 showingDropdown = false;
509 dropdownClosed=true;
510 }
511 else if(event.keyCode == Keyboard.ENTER)
512 {
513 if(keepLocalHistory && dataProvider.length==0)
514 addToLocalHistory();
515 }
516 else if(lookAhead && KeyUtils.isRemoveKey(event))
517 removeHighlight = true;
518 }
519 else
520 if(event.ctrlKey && event.keyCode == Keyboard.UP)
521 dropdownClosed=true;
522
523
524 prevIndex = selectedIndex;
525 }
526
527 /**
528 * @private
529 */
530 override protected function measure():void
531 {
532 super.measure();
533 measuredWidth = mx.core.UIComponent.DEFAULT_MEASURED_WIDTH;
534 }
535
536 /**
537 * @private
538 */
539 override protected function updateDisplayList(unscaledWidth:Number,
540 unscaledHeight:Number):void
541 {
542
543 super.updateDisplayList(unscaledWidth, unscaledHeight);
544
545 //An UP "keydown" event on the top-most item in the drop
546 //down list otherwise changes the text in the text field to ""
547 if(selectedIndex == -1)
548 textInput.text = typedText;
549
550 if(dropdown)
551 {
552 if(typedTextChanged)
553 {
554 //This is needed because a call to super.updateDisplayList() set the text
555 // in the textInput to "" and thus the value
556 //typed by the user losts
557 if(lookAhead && showDropdown && typedText!="" && !removeHighlight)
558 {
559 var label:String = itemToLabel(collection[0]);
560 var index:Number = label.toLowerCase().indexOf(_typedText.toLowerCase());
561 if(index==0)
562 {
563 textInput.text = _typedText+label.substr(_typedText.length);
564 textInput.setSelection(textInput.text.length,_typedText.length);
565 }
566 else
567 {
568 textInput.text = _typedText;
569 textInput.setSelection(cursorPosition, cursorPosition);
570 removeHighlight = false;
571 }
572
573 }
574 else
575 {
576 textInput.text = _typedText;
577 textInput.setSelection(cursorPosition, cursorPosition);
578 removeHighlight = false;
579 }
580
581 typedTextChanged= false;
582 }
583 else if(typedText)
584 //Sets the selection when user navigates the suggestion list through
585 //arrows keys.
586 textInput.setSelection(_typedText.length,textInput.text.length);
587 }
588 if(showDropdown && !dropdown.visible)
589 {
590 //This is needed to control the open duration of the dropdown
591 super.open();
592 showDropdown = false;
593 showingDropdown = true;
594
595 if(dropdownClosed)
596 dropdownClosed=false;
597 }
598 }
599
600
601 /**
602 * @private
603 */
604 override protected function textInput_changeHandler(event:Event):void
605 {
606 super.textInput_changeHandler(event);
607 //Stores the text typed by the user in a variable
608 typedText=text;
609 if (text.charAt(text.length - 1) == ' ') {
610 typedText = text.slice(0, text.length - 1);
611 }
612 }
613
614 protected function textInput_keyHandler(event:KeyboardEvent):void
615 {
616 if (event.keyCode == Keyboard.SPACE && showingDropdown && selectedIndex != -1 && !event.ctrlKey) {
617// event.preventDefault();
618 dispatchEvent(new FlexEvent(FlexEvent.ENTER));
619 }
620 }
621
622 //--------------------------------------------------------------------------
623 //
624 // Methods
625 //
626 //--------------------------------------------------------------------------
627
628 /**
629 * @private
630 * If keepLocalHistory is enabled, stores the text typed
631 * by the user in the local history on the client machine
632 */
633 private function addToLocalHistory():void
634 {
635 if (id != null && id != "" && text != null && text != "")
636 {
637 var so:SharedObject = SharedObject.getLocal("AutoCompleteData");
638
639 var savedData : Array = so.data.suggestions;
640 //No shared object has been created so far
641 if (savedData == null)
642 savedData = new Array();
643
644 var i:Number=0;
645 var flag:Boolean=false;
646 //Check if this entry is there in the previously saved shared object data
647 for(i=0;i<savedData.length;i++)
648 if(savedData[i]==text)
649 {
650 flag=true;
651 break;
652 }
653 if(!flag)
654 {
655 //Also ensure it is not there in the dataProvider
656 for(i=0;i<collection.length;i++)
657 if(defaultFilterFunction(itemToLabel(ListCollectionView(collection).getItemAt(i)),text))
658 {
659 flag=true;
660 break;
661 }
662 }
663 if(!flag)
664 savedData.push(text);
665
666 so.data.suggestions = savedData;
667 //write the shared object in the .sol file
668 so.flush();
669 }
670 }
671 /**
672 * @private
673 */
674 private function defaultFilterFunction(element:*, text:String):Boolean
675 {
676 var label:String = itemToLabel(element);
677 return (label.toLowerCase().substring(0,text.length) == text.toLowerCase());
678 }
679 /**
680 * @private
681 */
682
683 private function templateFilterFunction(element:*):Boolean
684 {
685 var flag:Boolean=false;
686 if(filterFunction!=null)
687 flag=filterFunction(element,typedText);
688 return flag;
689 }
690
691 /**
692 * @private
693 * Updates the dataProvider used for showing suggestions
694 */
695 private function updateDataProvider():void
696 {
697 dataProvider = tempCollection;
698 collection.filterFunction = templateFilterFunction;
699 collection.refresh();
700
701 //In case there are no suggestions, check there is something in the localHistory
702 if(collection.length==0 && keepLocalHistory)
703 {
704 var so:SharedObject = SharedObject.getLocal("AutoCompleteData");
705 usingLocalHistory = true;
706 dataProvider = so.data.suggestions;
707 usingLocalHistory = false;
708 collection.filterFunction = templateFilterFunction;
709 collection.refresh();
710 }
711 }
712
713 /**
714 * @protected
715 * Puts focus on the autocomplete component and fires an accessibility event
716 * so that it can be read by a screen reader.
717 *
718 * added by: jtorreno 04-30-2008
719 */
720 override protected function focusInHandler(event:FocusEvent):void
721 {
722 super.setFocus();
723
724 dispatchEvent(new Event("onFocus"));
725 }
726
727 /**
728 * Activates accessibility
729 * @protected
730 */
731 override protected function initializeAccessibility():void
732 {
733
734 if (AutoComplete.createAccessibilityImplementation != null)
735 AutoComplete.createAccessibilityImplementation(this);
736 }
737
738 }
739}
Note: See TracBrowser for help on using the repository browser.