1////////////////////////////////////////////////////////////////////////////////
2//
3//  ADOBE SYSTEMS INCORPORATED
4//  Copyright 2003-2007 Adobe Systems Incorporated
5//  All Rights Reserved.
6//
7//  NOTICE: Adobe permits you to use, modify, and distribute this file
8//  in accordance with the terms of the license agreement accompanying it.
9//
10////////////////////////////////////////////////////////////////////////////////
11
12package mx.managers
13{
14
15import flash.display.DisplayObject;
16import flash.display.DisplayObjectContainer;
17import flash.display.InteractiveObject;
18import flash.display.Sprite;
19import flash.events.Event;
20import flash.events.FocusEvent;
21import flash.events.IEventDispatcher;
22import flash.events.KeyboardEvent;
23import flash.events.MouseEvent;
24import flash.system.Capabilities;
25import flash.text.TextField;
26import flash.ui.Keyboard;
27
28import mx.core.FlexSprite;
29import mx.core.IButton;
30import mx.core.IChildList;
31import mx.core.IRawChildrenContainer;
32import mx.core.ISWFBridgeProvider;
33import mx.core.ISWFLoader;
34import mx.core.IToggleButton;
35import mx.core.IUIComponent;
36import mx.core.SWFBridgeGroup;
37import mx.core.mx_internal;
38import mx.events.FlexEvent;
39import mx.events.FocusRequestDirection;
40import mx.events.SWFBridgeEvent;
41import mx.events.SWFBridgeRequest;
42
43use namespace mx_internal;
44
45/**
46 *  The FocusManager class manages the focus on components in response to mouse
47 *  activity or keyboard activity (Tab key).  There can be several FocusManager
48 *  instances in an application.  Each FocusManager instance
49 *  is responsible for a set of components that comprise a "tab loop".  If you
50 *  hit Tab enough times, focus traverses through a set of components and
51 *  eventually get back to the first component that had focus.  That is a "tab loop"
52 *  and a FocusManager instance manages that loop.  If there are popup windows
53 *  with their own set of components in a "tab loop" those popup windows will have
54 *  their own FocusManager instances.  The main application always has a
55 *  FocusManager instance.
56 *
57 *  <p>The FocusManager manages focus from the "component level".
58 *  In Flex, a UITextField in a component is the only way to allow keyboard entry
59 *  of text. To the Flash Player or AIR, that UITextField has focus. However, from the
60 *  FocusManager's perspective the component that parents the UITextField has focus.
61 *  Thus there is a distinction between component-level focus and player-level focus.
62 *  Application developers generally only have to deal with component-level focus while
63 *  component developers must understand player-level focus.</p>
64 *
65 *  <p>All components that can be managed by the FocusManager must implement
66 *  mx.managers.IFocusManagerComponent, whereas objects managed by player-level focus do not.</p>
67 *
68 *  <p>The FocusManager also managers the concept of a defaultButton, which is
69 *  the Button on a form that dispatches a click event when the Enter key is pressed
70 *  depending on where focus is at that time.</p>
71 */
72public class FocusManager implements IFocusManager
73{
74    include "../core/Version.as";
75
76	//--------------------------------------------------------------------------
77	//
78	//  Class constants
79	//
80	//--------------------------------------------------------------------------
81
82	/**
83	 * @private
84	 *
85	 * Default value of parameter, ignore.
86	 */
87	private static const FROM_INDEX_UNSPECIFIED:int = -2;
88
89    //--------------------------------------------------------------------------
90    //
91    //  Constructor
92    //
93    //--------------------------------------------------------------------------
94
95    /**
96     *  Constructor.
97     *
98     *  <p>A FocusManager manages the focus within the children of an IFocusManagerContainer.
99     *  It installs itself in the IFocusManagerContainer during execution
100     *  of the constructor.</p>
101     *
102     *  @param container An IFocusManagerContainer that hosts the FocusManager.
103     *
104     *  @param popup If <code>true</code>, indicates that the container
105     *  is a popup component and not the main application.
106     */
107    public function FocusManager(container:IFocusManagerContainer, popup:Boolean = false)
108    {
109        super();
110
111		this.popup = popup;
112
113        browserMode = Capabilities.playerType == "ActiveX" && !popup;
114
115        container.focusManager = this; // this property name is reserved in the parent
116
117        // trace("FocusManager constructor " + container + ".focusManager");
118
119        _form = container;
120
121        focusableObjects = [];
122
123        focusPane = new FlexSprite();
124        focusPane.name = "focusPane";
125
126        addFocusables(DisplayObject(container));
127
128        // Listen to the stage so we know when the root application is loaded.
129        container.addEventListener(Event.ADDED, addedHandler);
130        container.addEventListener(Event.REMOVED, removedHandler);
131        container.addEventListener(FlexEvent.SHOW, showHandler);
132        container.addEventListener(FlexEvent.HIDE, hideHandler);
133
134        //special case application and window
135        if (container.systemManager is SystemManager)
136        {
137            // special case application.  It shouldn't need to be made
138            // active and because we defer appCreationComplete, this
139            // would steal focus back from any popups created during
140            // instantiation
141            if (container != SystemManager(container.systemManager).application)
142                container.addEventListener(FlexEvent.CREATION_COMPLETE,
143                                       creationCompleteHandler);
144        }
145
146        // Make sure the SystemManager is running so it can tell us about
147        // mouse clicks and stage size changes.
148		try
149		{
150        	container.systemManager.addFocusManager(container); // build a message that does the equal
151
152            var sm:ISystemManager = form.systemManager;
153
154            // Set up our swfBridgeGroup. If this is a pop up then the parent
155            // bridge is empty, otherwise its the form's system manager's bridge.
156            swfBridgeGroup = new SWFBridgeGroup(sm);
157            if (!popup)
158                swfBridgeGroup.parentBridge = sm.swfBridgeGroup.parentBridge;
159
160			// add ourselves to our parent focus manager if this is a bridged
161			// application not a dialog or other popup.
162			if (sm.useSWFBridge())
163			{
164			    sm.addEventListener(SWFBridgeEvent.BRIDGE_APPLICATION_UNLOADING, removeFromParentBridge);
165
166				// have the child listen to move requests from the parent.
167				var bridge:IEventDispatcher = swfBridgeGroup.parentBridge;
168		       	if (bridge)
169	    	   	{
170	       			bridge.addEventListener(SWFBridgeRequest.MOVE_FOCUS_REQUEST, focusRequestMoveHandler);
171                    bridge.addEventListener(SWFBridgeRequest.SET_SHOW_FOCUS_INDICATOR_REQUEST,
172                                            setShowFocusIndicatorRequestHandler);
173	       		}
174
175	   			// add listener activate/deactivate requests
176	   			if (bridge && !(form.systemManager is SystemManagerProxy))
177	   			{
178	   				bridge.addEventListener(SWFBridgeRequest.ACTIVATE_FOCUS_REQUEST, focusRequestActivateHandler);
179	   				bridge.addEventListener(SWFBridgeRequest.DEACTIVATE_FOCUS_REQUEST, focusRequestDeactivateHandler);
180			   		bridge.addEventListener(SWFBridgeEvent.BRIDGE_FOCUS_MANAGER_ACTIVATE,
181			   				    		    bridgeEventActivateHandler);
182	   			}
183
184	   			// listen when the container has been added to the stage so we can add the focusable
185	   			// children
186	   			container.addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
187			}
188		}
189		catch (e:Error)
190		{
191			// ignore null pointer errors caused by container using a
192			// systemManager from another sandbox.
193		}
194    }
195
196    //--------------------------------------------------------------------------
197    //
198    //  Variables
199    //
200    //--------------------------------------------------------------------------
201
202    private var LARGE_TAB_INDEX:int = 99999;
203
204    private var calculateCandidates:Boolean = true;
205
206    /**
207     *  @private
208     *  We track whether we've been last activated or saw a TAB
209     *  This is used in browser tab management
210     */
211    private var lastAction:String;
212
213    /**
214     *  @private
215     *  Tab management changes based on whether were in a browser or not
216     *  This value is also affected by whether you are a modal dialog or not
217     */
218    public var browserMode:Boolean;
219
220    /**
221     *  @private
222     *  Tab management changes based on whether were in a browser or not
223     *  If non-null, this is the object that will
224     *  lose focus to the browser
225     */
226    private var browserFocusComponent:InteractiveObject;
227
228    /**
229     *  @private
230     *  Total set of all objects that can receive focus
231     *  but might be disabled or invisible.
232     */
233    private var focusableObjects:Array;
234
235    /**
236     *  @private
237     *  Filtered set of objects that can receive focus right now.
238     */
239    private var focusableCandidates:Array;
240
241    /**
242     *  @private
243     */
244    private var activated:Boolean = false;
245
246    /**
247    * 	@private
248    *
249    * 	true if focus was changed to one of focusable objects. False if focus passed to
250    * 	the browser.
251    */
252	private var focusChanged:Boolean;
253
254    /**
255	 * 	@private
256	 *
257	 * 	if non-null, the location to move focus from instead of the object
258	 *  that has focus in the stage.
259	 */
260	 private var fauxFocus:DisplayObject;
261
262	 /**
263	  *  @private
264	  *
265	  *  The focus manager maintains its own bridges so a focus manager in a pop
266	  *  up can move focus to another focus manager in the same pop up. That is,
267	  *  A pop ups can be a collection of focus managers working together just
268	  *  as is done in the System Manager's document.
269	  */
270	 private var swfBridgeGroup:SWFBridgeGroup;
271
272	/**
273	 * @private
274	 *
275	 * bridge handle of the last active focus manager.
276	 */
277     private var lastActiveFocusManager:FocusManager;
278
279	 /**
280	 * @private
281	 *
282	 * Test if the focus was set locally in this focus manager (true) or
283	 * if focus was transfer to another focus manager (false)
284	 */
285	 private var focusSetLocally:Boolean;
286
287	 /**
288	 * @private
289	 *
290	 * True if this focus manager is a popup, false if it is a main application.
291	 *
292	 */
293	 private var popup:Boolean;
294
295	 /**
296	  *  @private
297	  *
298	  *  Used when a the skip parameter can't be passed into
299	  *  dispatchEventFromSWFBridges() because the caller doesn't take
300	  *  a skip parameter.
301	  */
302	 private var skipBridge:IEventDispatcher;
303
304	//--------------------------------------------------------------------------
305    //
306    //  Properties
307    //
308    //--------------------------------------------------------------------------
309
310    //----------------------------------
311    //  showFocusIndicator
312    //----------------------------------
313
314    /**
315     *  @private
316     *  Storage for the showFocusIndicator property.
317     */
318    private var _showFocusIndicator:Boolean = false;
319
320    /**
321     *  @inheritDoc
322     */
323    public function get showFocusIndicator():Boolean
324    {
325        return _showFocusIndicator;
326    }
327
328    /**
329     *  @private
330     */
331    public function set showFocusIndicator(value:Boolean):void
332    {
333        var changed:Boolean = _showFocusIndicator != value;
334
335        _showFocusIndicator = value;
336
337        if (changed && !popup && form.systemManager.swfBridgeGroup)
338            dispatchSetShowFocusIndicatorRequest(value, null);
339    }
340
341    //----------------------------------
342    //  defaultButton
343    //----------------------------------
344
345    /**
346     *  @private
347     *  The current default button.
348     */
349    private var defButton:IButton;
350
351    /**
352     *  @private
353     */
354    private var _defaultButton:IButton;
355
356    /**
357     *  @inheritDoc
358     */
359    public function get defaultButton():IButton
360    {
361		return _defaultButton;
362    }
363
364    /**
365     *  @private
366     *  We don't type the value as Button for dependency reasons
367     */
368    public function set defaultButton(value:IButton):void
369    {
370		var button:IButton = value ? IButton(value) : null;
371
372        if (button != _defaultButton)
373        {
374            if (_defaultButton)
375                _defaultButton.emphasized = false;
376
377            if (defButton)
378                defButton.emphasized = false;
379
380            _defaultButton = button;
381
382            if (_lastFocus && _lastFocus is IButton &&
383                !(_lastFocus is IToggleButton))
384            {
385            	defButton = _lastFocus as IButton;
386            	defButton.emphasized = true;
387            }
388            else
389            {
390                defButton = button;
391
392                if (button)
393                    button.emphasized = true;
394            }
395
396        }
397    }
398
399    //----------------------------------
400    //  defaultButtonEnabled
401    //----------------------------------
402
403    /**
404     *  @private
405     *  Storage for the defaultButtonEnabled property.
406     */
407    private var _defaultButtonEnabled:Boolean = true;
408
409    /**
410     *  @inheritDoc
411     */
412    public function get defaultButtonEnabled():Boolean
413    {
414        return _defaultButtonEnabled;
415    }
416
417    /**
418     *  @private
419     */
420    public function set defaultButtonEnabled(value:Boolean):void
421    {
422        _defaultButtonEnabled = value;
423    }
424
425    //----------------------------------
426    //  focusPane
427    //----------------------------------
428
429    /**
430     *  @private
431     *  Storage for the focusPane property.
432     */
433    private var _focusPane:Sprite;
434
435    /**
436     *  @inheritDoc
437     */
438    public function get focusPane():Sprite
439    {
440        return _focusPane;
441    }
442
443    /**
444     *  @private
445     */
446    public function set focusPane(value:Sprite):void
447    {
448        _focusPane = value;
449    }
450
451    //----------------------------------
452    //  form
453    //----------------------------------
454
455    /**
456     *  @private
457     *  Storage for the form property.
458     */
459    private var _form:IFocusManagerContainer;
460
461    /**
462     *  @private
463     *  The form is the property where we store the IFocusManagerContainer
464     *  that hosts this FocusManager.
465     */
466    mx_internal function get form():IFocusManagerContainer
467    {
468        return _form;
469    }
470
471    /**
472     *  @private
473     */
474    mx_internal function set form (value:IFocusManagerContainer):void
475    {
476        _form = value;
477    }
478
479
480    //----------------------------------
481    //  _lastFocus
482    //----------------------------------
483
484    /**
485     *  @private
486     *  the object that last had focus
487     */
488    private var _lastFocus:IFocusManagerComponent;
489
490
491	/**
492	 * 	@private
493	 */
494	mx_internal function get lastFocus():IFocusManagerComponent
495	{
496		return _lastFocus;
497	}
498
499    //----------------------------------
500    //  nextTabIndex
501    //----------------------------------
502
503    /**
504     *  @inheritDoc
505     */
506    public function get nextTabIndex():int
507    {
508        return getMaxTabIndex() + 1;
509    }
510
511    /**
512     *  Gets the highest tab index currently used in this Focus Manager's form or subform.
513     *
514     *  @return Highest tab index currently used.
515     */
516    private function getMaxTabIndex():int
517    {
518        var z:Number = 0;
519
520        var n:int = focusableObjects.length;
521        for (var i:int = 0; i < n; i++)
522        {
523            var t:Number = focusableObjects[i].tabIndex;
524            if (!isNaN(t))
525                z = Math.max(z, t);
526        }
527
528        return z;
529    }
530
531    //--------------------------------------------------------------------------
532    //
533    //  Methods
534    //
535    //--------------------------------------------------------------------------
536
537    /**
538     *  @inheritDoc
539     */
540    public function getFocus():IFocusManagerComponent
541    {
542        var o:InteractiveObject = form.systemManager.stage.focus;
543        return findFocusManagerComponent(o);
544    }
545
546    /**
547     *  @inheritDoc
548     */
549    public function setFocus(o:IFocusManagerComponent):void
550    {
551        // trace("FM " + this + " setting focus to " + o);
552
553        o.setFocus();
554
555        focusSetLocally = true;
556
557        // trace("FM set focus");
558    }
559
560    /**
561     *  @private
562     */
563    private function focusInHandler(event:FocusEvent):void
564    {
565        var target:InteractiveObject = InteractiveObject(event.target);
566        // trace("FocusManager focusInHandler in  = " + this._form.systemManager.loaderInfo.url);
567        // trace("FM " + this + " focusInHandler " + target);
568
569		// if the target is in a bridged application, let it handle the click.
570		var sm:ISystemManager = form.systemManager;
571   		if (sm.isDisplayObjectInABridgedApplication(DisplayObject(event.target)))
572   			return;
573
574        if (isParent(DisplayObjectContainer(form), target))
575        {
576            // trace("FM " + this + " setting last focus " + target);
577            _lastFocus = findFocusManagerComponent(InteractiveObject(target));
578
579			// handle default button here
580			// we can't check for Button because of cross-versioning so
581			// for now we just check for an emphasized property
582			if (_lastFocus is IButton && !(_lastFocus is IToggleButton))
583			{
584				var x:IButton = _lastFocus as IButton;
585				// if we have marked some other button as a default button
586				if (defButton)
587				{
588					// change it to be this button
589					defButton.emphasized = false;
590					defButton = x;
591					x.emphasized = true;
592				}
593			}
594			else
595			{
596				// restore the default button to be the original one
597				if (defButton && defButton != _defaultButton)
598				{
599					defButton.emphasized = false;
600					defButton = _defaultButton;
601                    if (_defaultButton)
602					    _defaultButton.emphasized = true;
603				}
604			}
605		}
606    }
607
608    /**
609     *  @private  Useful for debugging
610     */
611    private function focusOutHandler(event:FocusEvent):void
612    {
613        var target:InteractiveObject = InteractiveObject(event.target);
614        // trace("FocusManager focusOutHandler in  = " + this._form.systemManager.loaderInfo.url);
615        // trace("FM " + this + " focusOutHandler " + target);
616    }
617
618    /**
619     *  @private
620     *  restore focus to whoever had it last
621     */
622    private function activateHandler(event:Event):void
623    {
624//        var target:InteractiveObject = InteractiveObject(event.target);
625        // trace("FM " + this + " activateHandler ", _lastFocus);
626
627		// restore focus if this focus manager had last focus
628	    if (_lastFocus && !browserMode)
629	    	_lastFocus.setFocus();
630	    lastAction = "ACTIVATE";
631    }
632
633    /**
634     *  @private  Useful for debugging
635     */
636    private function deactivateHandler(event:Event):void
637    {
638        // var target:InteractiveObject = InteractiveObject(event.target);
639        // trace("FM " + this + " deactivateHandler ", _lastFocus);
640    }
641
642    /**
643     *  @inheritDoc
644     */
645    public function showFocus():void
646    {
647        if (!showFocusIndicator)
648        {
649            showFocusIndicator = true;
650            if (_lastFocus)
651                _lastFocus.drawFocus(true);
652        }
653    }
654
655    /**
656     *  @inheritDoc
657     */
658    public function hideFocus():void
659    {
660        // trace("FOcusManger " + this + " Hide Focus");
661        if (showFocusIndicator)
662        {
663            showFocusIndicator = false;
664            if (_lastFocus)
665                _lastFocus.drawFocus(false);
666        }
667        // trace("END FOcusManger Hide Focus");
668    }
669
670    /**
671     *  The SystemManager activates and deactivates a FocusManager
672     *  if more than one IFocusManagerContainer is visible at the same time.
673     *  If the mouse is clicked in an IFocusManagerContainer with a deactivated
674     *  FocusManager, the SystemManager will call
675     *  the <code>activate()</code> method on that FocusManager.
676     *  The FocusManager that was activated will have its <code>deactivate()</code> method
677     *  called prior to the activation of another FocusManager.
678     *
679     *  <p>The FocusManager adds event handlers that allow it to monitor
680     *  focus related keyboard and mouse activity.</p>
681     */
682    public function activate():void
683    {
684        // we can get a double activation if we're popping up and becoming visible
685        // like the second time a menu appears
686        if (activated)
687        {
688        	// trace("FocusManager is already active " + this);
689            return;
690        }
691
692        //trace("FocusManager activating = " + this._form.systemManager.loaderInfo.url);
693        //trace("FocusManager activating " + this);
694
695        // listen for focus changes, use weak references for the stage
696		// form.systemManager can be null if the form is created in a sandbox and
697		// added as a child to the root system manager.
698		var sm:ISystemManager = form.systemManager;
699		if (sm)
700		{
701			if (sm.isTopLevelRoot())
702			{
703		        sm.stage.addEventListener(FocusEvent.MOUSE_FOCUS_CHANGE, mouseFocusChangeHandler, false, 0, true);
704		        sm.stage.addEventListener(FocusEvent.KEY_FOCUS_CHANGE, keyFocusChangeHandler, false, 0, true);
705	    	    sm.stage.addEventListener(Event.ACTIVATE, activateHandler, false, 0, true);
706	        	sm.stage.addEventListener(Event.DEACTIVATE, deactivateHandler, false, 0, true);
707	  		}
708	  		else
709	  		{
710		        sm.addEventListener(FocusEvent.MOUSE_FOCUS_CHANGE, mouseFocusChangeHandler, false, 0, true);
711		        sm.addEventListener(FocusEvent.KEY_FOCUS_CHANGE, keyFocusChangeHandler, false, 0, true);
712	    	    sm.addEventListener(Event.ACTIVATE, activateHandler, false, 0, true);
713	        	sm.addEventListener(Event.DEACTIVATE, deactivateHandler, false, 0, true);
714	  		}
715		}
716
717        form.addEventListener(FocusEvent.FOCUS_IN, focusInHandler, true);
718        form.addEventListener(FocusEvent.FOCUS_OUT, focusOutHandler, true);
719        form.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
720        form.addEventListener(KeyboardEvent.KEY_DOWN, defaultButtonKeyHandler);
721        // listen for default button in Capture phase. Some components like TextInput
722        // and Accordion stop the Enter key from propagating in the Bubble phase.
723        form.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler, true);
724
725        activated = true;
726
727        // Restore focus to the last control that had it if there was one.
728        if (_lastFocus)
729            setFocus(_lastFocus);
730
731		// activate children in compatibility mode or in sandboxes.
732    	dispatchEventFromSWFBridges(new SWFBridgeRequest(SWFBridgeRequest.ACTIVATE_FOCUS_REQUEST), skipBridge);
733
734    }
735
736    /**
737     *  The SystemManager activates and deactivates a FocusManager
738     *  if more than one IFocusManagerContainer is visible at the same time.
739     *  If the mouse is clicked in an IFocusManagerContainer with a deactivated
740     *  FocusManager, the SystemManager will call
741     *  the <code>activate()</code> method on that FocusManager.
742     *  The FocusManager that was activated will have its <code>deactivate()</code> method
743     *  called prior to the activation of another FocusManager.
744     *
745     *  <p>The FocusManager removes event handlers that allow it to monitor
746     *  focus related keyboard and mouse activity.</p>
747     */
748    public function deactivate():void
749    {
750        // trace("FocusManager deactivating " + this);
751        //trace("FocusManager deactivating = " + this._form.systemManager.loaderInfo.url);
752
753        // listen for focus changes
754		var sm:ISystemManager = form.systemManager;
755        if (sm)
756        {
757			if (sm.isTopLevelRoot())
758			{
759		        sm.stage.removeEventListener(FocusEvent.MOUSE_FOCUS_CHANGE, mouseFocusChangeHandler);
760		        sm.stage.removeEventListener(FocusEvent.KEY_FOCUS_CHANGE, keyFocusChangeHandler);
761	    	    sm.stage.removeEventListener(Event.ACTIVATE, activateHandler);
762	        	sm.stage.removeEventListener(Event.DEACTIVATE, deactivateHandler);
763	  		}
764	  		else
765	  		{
766		        sm.removeEventListener(FocusEvent.MOUSE_FOCUS_CHANGE, mouseFocusChangeHandler);
767		        sm.removeEventListener(FocusEvent.KEY_FOCUS_CHANGE, keyFocusChangeHandler);
768	    	    sm.removeEventListener(Event.ACTIVATE, activateHandler);
769	        	sm.removeEventListener(Event.DEACTIVATE, deactivateHandler);
770	  		}
771        }
772
773        form.removeEventListener(FocusEvent.FOCUS_IN, focusInHandler, true);
774        form.removeEventListener(FocusEvent.FOCUS_OUT, focusOutHandler, true);
775        form.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
776        form.removeEventListener(KeyboardEvent.KEY_DOWN, defaultButtonKeyHandler);
777        // stop listening for default button in Capture phase
778        form.removeEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler, true);
779
780        activated = false;
781
782		// deactivate children in compatibility mode or in sandboxes.
783		dispatchEventFromSWFBridges(new SWFBridgeRequest(SWFBridgeRequest.DEACTIVATE_FOCUS_REQUEST), skipBridge);
784    }
785
786    /**
787     *  @inheritDoc
788     */
789    public function findFocusManagerComponent(
790                            o:InteractiveObject):IFocusManagerComponent
791    {
792    	return findFocusManagerComponent2(o) as IFocusManagerComponent;
793    }
794
795
796    /**
797    * @private
798    *
799    * This version of the method differs from the old one to support SWFLoader
800    * being in the focusableObjects list but not being a component that
801    * gets focus. SWFLoader is in the list of focusable objects so
802    * focus may be passed over a bridge to the components on the other
803    * side of the bridge.
804    */
805    private function findFocusManagerComponent2(
806                            o:InteractiveObject):DisplayObject
807
808    {
809    	try
810    	{
811	        while (o)
812	        {
813	            if ((o is IFocusManagerComponent && IFocusManagerComponent(o).focusEnabled) ||
814	            	 o is ISWFLoader)
815	                return o;
816
817	            o = o.parent;
818	        }
819	    }
820	    catch (error:SecurityError)
821	    {
822	    	// can happen in a loaded child swf
823	    	// trace("findFocusManagerComponent: handling security error");
824	    }
825
826        // tab was set somewhere else
827        return null;
828    }
829
830	/**
831	 *  @inheritDoc
832	 */
833	public function moveFocus(direction:String, fromDisplayObject:DisplayObject = null):void
834	{
835	    if (direction == FocusRequestDirection.TOP)
836	    {
837	        setFocusToTop();
838	        return;
839	    }
840
841        if (direction == FocusRequestDirection.BOTTOM)
842        {
843            setFocusToBottom();
844            return;
845        }
846
847		var keyboardEvent:KeyboardEvent = new KeyboardEvent(KeyboardEvent.KEY_DOWN);
848		keyboardEvent.keyCode = Keyboard.TAB;
849		keyboardEvent.shiftKey = (direction == FocusRequestDirection.FORWARD) ? false : true;
850		fauxFocus = fromDisplayObject;
851		keyDownHandler(keyboardEvent);
852
853    	var focusEvent:FocusEvent = new FocusEvent(FocusEvent.KEY_FOCUS_CHANGE);
854    	focusEvent.keyCode = Keyboard.TAB;
855    	focusEvent.shiftKey = (direction == FocusRequestDirection.FORWARD) ? false : true;
856
857    	keyFocusChangeHandler(focusEvent);
858
859		fauxFocus = null;
860	}
861
862    /**
863     *  @private
864     *  Returns true if p is a parent of o.
865     */
866    private function isParent(p:DisplayObjectContainer, o:DisplayObject):Boolean
867    {
868        if (p is IRawChildrenContainer)
869            return IRawChildrenContainer(p).rawChildren.contains(o);
870
871        return p.contains(o);
872    }
873
874    private function isEnabledAndVisible(o:DisplayObject):Boolean
875    {
876        var formParent:DisplayObjectContainer = DisplayObject(form).parent;
877
878        while (o != formParent)
879        {
880            if (o is IUIComponent)
881                if (!IUIComponent(o).enabled)
882                    return false;
883            if (!o.visible)
884                return false;
885            o = o.parent;
886        }
887        return true;
888    }
889
890    /**
891     *  @private
892     */
893    private function sortByTabIndex(a:InteractiveObject, b:InteractiveObject):int
894    {
895        var aa:int = a.tabIndex;
896        var bb:int = b.tabIndex;
897
898        if (aa == -1)
899            aa = int.MAX_VALUE;
900        if (bb == -1)
901            bb = int.MAX_VALUE;
902
903        return (aa > bb ? 1 :
904                aa < bb ? -1 : sortByDepth(DisplayObject(a), DisplayObject(b)));
905    }
906
907    /**
908     *  @private
909     */
910    private function sortFocusableObjectsTabIndex():void
911    {
912        //trace("FocusableObjectsTabIndex");
913
914        focusableCandidates = [];
915
916        var n:int = focusableObjects.length;
917        for (var i:int = 0; i < n; i++)
918        {
919            var c:IFocusManagerComponent = focusableObjects[i] as IFocusManagerComponent;
920            if ((c && c.tabIndex && !isNaN(Number(c.tabIndex))) ||
921                 focusableObjects[i] is ISWFLoader)
922            {
923                // if we get here, it is a candidate
924                focusableCandidates.push(focusableObjects[i]);
925            }
926        }
927
928        focusableCandidates.sort(sortByTabIndex);
929    }
930
931    /**
932     *  @private
933     */
934    private function sortByDepth(aa:DisplayObject, bb:DisplayObject):Number
935    {
936        var val1:String = "";
937        var val2:String = "";
938        var index:int;
939        var tmp:String;
940        var tmp2:String;
941        var zeros:String = "0000";
942
943        var a:DisplayObject = DisplayObject(aa);
944        var b:DisplayObject = DisplayObject(bb);
945
946        while (a != DisplayObject(form) && a.parent)
947        {
948            index = getChildIndex(a.parent, a);
949            tmp = index.toString(16);
950            if (tmp.length < 4)
951            {
952                tmp2 = zeros.substring(0, 4 - tmp.length) + tmp;
953            }
954            val1 = tmp2 + val1;
955            a = a.parent;
956        }
957        while (b != DisplayObject(form) && b.parent)
958        {
959            index = getChildIndex(b.parent, b);
960            tmp = index.toString(16);
961            if (tmp.length < 4)
962            {
963                tmp2 = zeros.substring(0, 4 - tmp.length) + tmp;
964            }
965            val2 = tmp2 + val2;
966            b = b.parent;
967        }
968
969        return val1 > val2 ? 1 : val1 < val2 ? -1 : 0;
970    }
971
972    private function getChildIndex(parent:DisplayObjectContainer, child:DisplayObject):int
973    {
974        try
975        {
976            return parent.getChildIndex(child);
977        }
978        catch(e:Error)
979        {
980            if (parent is IRawChildrenContainer)
981                return IRawChildrenContainer(parent).rawChildren.getChildIndex(child);
982            throw e;
983        }
984        throw new Error("FocusManager.getChildIndex failed");   // shouldn't ever get here
985    }
986
987    /**
988     *  @private
989     *  Calculate what focusableObjects are valid tab candidates.
990     */
991    private function sortFocusableObjects():void
992    {
993        // trace("FocusableObjects " + focusableObjects.length.toString());
994        focusableCandidates = [];
995
996        var n:int = focusableObjects.length;
997        for (var i:int = 0; i < n; i++)
998        {
999            var c:InteractiveObject = focusableObjects[i];
1000            // trace("  " + c);
1001            if (c.tabIndex && !isNaN(Number(c.tabIndex)) && c.tabIndex > 0)
1002            {
1003                sortFocusableObjectsTabIndex();
1004                return;
1005            }
1006            focusableCandidates.push(c);
1007        }
1008
1009        focusableCandidates.sort(sortByDepth);
1010    }
1011
1012    /**
1013     *  Call this method to make the system
1014     *  think the Enter key was pressed and the defaultButton was clicked
1015     */
1016    mx_internal function sendDefaultButtonEvent():void
1017    {
1018        // trace("FocusManager.sendDefaultButtonEvent " + defButton);
1019        defButton.dispatchEvent(new MouseEvent("click"));
1020    }
1021
1022    /**
1023     *  @private
1024     *  Do a tree walk and add all children you can find.
1025     */
1026    private function addFocusables(o:DisplayObject, skipTopLevel:Boolean = false):void
1027    {
1028        // trace(">>addFocusables " + o);
1029        if ((o is IFocusManagerComponent) && !skipTopLevel)
1030        {
1031
1032			var addToFocusables:Boolean = false;
1033        	if (o is IFocusManagerComponent)
1034        	{
1035	            var focusable:IFocusManagerComponent = IFocusManagerComponent(o);
1036	            if (focusable.focusEnabled)
1037	            {
1038	                if (focusable.tabEnabled && isTabVisible(o))
1039	                {
1040						addToFocusables = true;
1041	                }
1042                }
1043            }
1044
1045			if (addToFocusables)
1046			{
1047               	if (focusableObjects.indexOf(o) == -1)
1048            	{
1049                    focusableObjects.push(o);
1050	                calculateCandidates = true;
1051    	            // trace("FM added " + o);
1052    	        }
1053                o.addEventListener("tabEnabledChange", tabEnabledChangeHandler);
1054                o.addEventListener("tabIndexChange", tabIndexChangeHandler);
1055   			}
1056
1057        }
1058
1059        if (o is DisplayObjectContainer)
1060        {
1061            var doc:DisplayObjectContainer = DisplayObjectContainer(o);
1062            // Even if they aren't focusable now,
1063            // listen in case they become later.
1064            o.addEventListener("tabChildrenChange", tabChildrenChangeHandler);
1065
1066            if (doc.tabChildren)
1067            {
1068                if (o is IRawChildrenContainer)
1069                {
1070                    // trace("using view rawChildren");
1071                    var rawChildren:IChildList = IRawChildrenContainer(o).rawChildren;
1072                    // recursively visit and add children of components
1073                    // we don't do this for containers because we get individual
1074                    // adds for the individual children
1075                    var i:int;
1076                    for (i = 0; i < rawChildren.numChildren; i++)
1077                    {
1078                        try
1079                        {
1080                            addFocusables(rawChildren.getChildAt(i));
1081                        }
1082                        catch(error:SecurityError)
1083                        {
1084                            // Ignore this child if we can't access it
1085                            // trace("addFocusables: ignoring security error getting child from rawChildren: " + error);
1086                        }
1087                    }
1088
1089                }
1090                else
1091                {
1092                    // trace("using container's children");
1093                    // recursively visit and add children of components
1094                    // we don't do this for containers because we get individual
1095                    // adds for the individual children
1096                    for (i = 0; i < doc.numChildren; i++)
1097                    {
1098                        try
1099                        {
1100                            addFocusables(doc.getChildAt(i));
1101                        }
1102                        catch(error:SecurityError)
1103                        {
1104                            // Ignore this child if we can't access it
1105                            // trace("addFocusables: ignoring security error getting child at document." + error);
1106                        }
1107                    }
1108                }
1109            }
1110        }
1111        // trace("<<addFocusables " + o);
1112    }
1113
1114    /**
1115     *  @private
1116     *  is it really tabbable?
1117     */
1118    private function isTabVisible(o:DisplayObject):Boolean
1119    {
1120        var s:DisplayObject = DisplayObject(form.systemManager);
1121        if (!s) return false;
1122
1123        var p:DisplayObjectContainer = o.parent;
1124        while (p && p != s)
1125        {
1126            if (!p.tabChildren)
1127                return false;
1128            p = p.parent;
1129        }
1130        return true;
1131    }
1132
1133    private function isValidFocusCandidate(o:DisplayObject, g:String):Boolean
1134    {
1135        if (!isEnabledAndVisible(o))
1136            return false;
1137
1138        if (o is IFocusManagerGroup)
1139        {
1140            // reject if it is in the same tabgroup
1141            var tg:IFocusManagerGroup = IFocusManagerGroup(o);
1142            if (g == tg.groupName) return false;
1143        }
1144        return true;
1145    }
1146
1147    private function getIndexOfFocusedObject(o:DisplayObject):int
1148    {
1149        if (!o)
1150            return -1;
1151
1152        var n:int = focusableCandidates.length;
1153        // trace(" focusableCandidates " + n);
1154        var i:int = 0;
1155        for (i = 0; i < n; i++)
1156        {
1157            // trace(" comparing " + focusableCandidates[i]);
1158            if (focusableCandidates[i] == o)
1159                return i;
1160        }
1161
1162        // no match?  try again with a slower match for certain
1163        // cases like DG editors
1164        for (i = 0; i < n; i++)
1165        {
1166            var iui:IUIComponent = focusableCandidates[i] as IUIComponent;
1167            if (iui && iui.owns(o))
1168                return i;
1169        }
1170
1171        return -1;
1172    }
1173
1174
1175    private function getIndexOfNextObject(i:int, shiftKey:Boolean, bSearchAll:Boolean, groupName:String):int
1176    {
1177        var n:int = focusableCandidates.length;
1178        var start:int = i;
1179
1180        while (true)
1181        {
1182            if (shiftKey)
1183                i--;
1184            else
1185                i++;
1186            if (bSearchAll)
1187            {
1188                if (shiftKey && i < 0)
1189                    break;
1190                if (!shiftKey && i == n)
1191                    break;
1192            }
1193            else
1194            {
1195                i = (i + n) % n;
1196                // came around and found the original
1197                if (start == i)
1198                    break;
1199                // if start is -1, set start to first valid value of i
1200                if (start == -1)
1201                    start = i;
1202            }
1203            // trace("testing " + focusableCandidates[i]);
1204            if (isValidFocusCandidate(focusableCandidates[i], groupName))
1205            {
1206                // trace(" stopped at " + i);
1207                var o:DisplayObject = DisplayObject(findFocusManagerComponent2(focusableCandidates[i]));
1208                if (o is IFocusManagerGroup)
1209                {
1210                    // look around to see if there's a selected member in the tabgroup
1211                    // otherwise use the first one we found.
1212                    var tg1:IFocusManagerGroup = IFocusManagerGroup(o);
1213                    for (var j:int = 0; j < focusableCandidates.length; j++)
1214                    {
1215                        var obj:DisplayObject = focusableCandidates[j];
1216                        if (obj is IFocusManagerGroup)
1217                        {
1218                            var tg2:IFocusManagerGroup = IFocusManagerGroup(obj);
1219                            if (tg2.groupName == tg1.groupName && tg2.selected)
1220                            {
1221                                // if objects of same group have different tab index
1222                                // skip you aren't selected.
1223                                if (InteractiveObject(obj).tabIndex != InteractiveObject(o).tabIndex && !tg1.selected)
1224                                    return getIndexOfNextObject(i, shiftKey, bSearchAll, groupName);
1225
1226                                i = j;
1227                                break;
1228                            }
1229                        }
1230                    }
1231
1232                }
1233                return i;
1234            }
1235        }
1236        return i;
1237    }
1238
1239    /**
1240     *  @private
1241     */
1242    private function setFocusToNextObject(event:FocusEvent):void
1243    {
1244     	focusChanged = false;
1245        if (focusableObjects.length == 0)
1246            return;
1247
1248		var focusInfo:FocusInfo = getNextFocusManagerComponent2(event.shiftKey, fauxFocus);
1249		// trace("winner = ", o);
1250
1251		// If we are about to wrap focus around, send focus back to the parent.
1252		if (!popup && (focusInfo.wrapped || !focusInfo.displayObject))
1253		{
1254			if (getParentBridge())
1255			{
1256				moveFocusToParent(event.shiftKey);
1257				return;
1258			}
1259		}
1260
1261		if (!focusInfo.displayObject)
1262		{
1263			event.preventDefault();
1264			return;
1265		}
1266
1267		setFocusToComponent(focusInfo.displayObject, event.shiftKey);
1268	}
1269
1270	private function setFocusToComponent(o:Object, shiftKey:Boolean):void
1271	{
1272		focusChanged = false;
1273		if (o)
1274		{
1275			if (o is ISWFLoader && ISWFLoader(o).swfBridge)
1276			{
1277				// send message to child swf to move focus.
1278				// trace("pass focus from " + this.form.systemManager.loaderInfo.url + " to " + DisplayObject(o).loaderInfo.url);
1279	    		var request:SWFBridgeRequest = new SWFBridgeRequest(SWFBridgeRequest.MOVE_FOCUS_REQUEST,
1280	    													false, true, null,
1281	    													shiftKey ? FocusRequestDirection.BOTTOM :
1282																	   FocusRequestDirection.TOP);
1283				var sandboxBridge:IEventDispatcher = ISWFLoader(o).swfBridge;
1284				if (sandboxBridge)
1285				{
1286    				sandboxBridge.dispatchEvent(request);
1287					focusChanged = request.data;
1288				}
1289			}
1290			else if (o is IFocusManagerComplexComponent)
1291			{
1292				IFocusManagerComplexComponent(o).assignFocus(shiftKey ? "bottom" : "top");
1293				focusChanged = true;
1294			}
1295			else if (o is IFocusManagerComponent)
1296			{
1297				setFocus(IFocusManagerComponent(o));
1298				focusChanged = true;
1299			}
1300
1301		}
1302
1303	}
1304
1305	/**
1306	 *  @private
1307	 */
1308	private function setFocusToTop():void
1309	{
1310		setFocusToNextIndex(-1, false);
1311	}
1312
1313	/**
1314	 *  @private
1315	 */
1316	private function setFocusToBottom():void
1317	{
1318		setFocusToNextIndex(focusableObjects.length, true);
1319	}
1320
1321	/**
1322	 *  @private
1323	 */
1324	private function setFocusToNextIndex(index:int, shiftKey:Boolean):void
1325	{
1326		if (focusableObjects.length == 0)
1327			return;
1328
1329        // I think we'll have time to do this here instead of at creation time
1330        // this makes and orders the focusableCandidates array
1331        if (calculateCandidates)
1332        {
1333            sortFocusableObjects();
1334            calculateCandidates = false;
1335        }
1336
1337		var focusInfo:FocusInfo = getNextFocusManagerComponent2(shiftKey, null, index);
1338
1339		// If we are about to wrap focus around, send focus back to the parent.
1340		if (!popup && focusInfo.wrapped)
1341		{
1342			if (getParentBridge())
1343			{
1344				moveFocusToParent(shiftKey);
1345				return;
1346			}
1347		}
1348
1349		setFocusToComponent(focusInfo.displayObject, shiftKey);
1350	}
1351
1352    /**
1353     *  @inheritDoc
1354     */
1355    public function getNextFocusManagerComponent(
1356                            backward:Boolean = false):IFocusManagerComponent
1357	{
1358		return getNextFocusManagerComponent2(backward, fauxFocus).displayObject as IFocusManagerComponent;
1359	}
1360
1361	/**
1362	 * Find the next object to set focus to.
1363	 *
1364	 * @param backward true if moving in the backwards in the tab order, false if moving forward.
1365	 * @param fromObject object to move focus from, if null move from the current focus.
1366	 * @param formIndex index to move focus from, if specified use fromIndex to find the
1367	 * 		   			object, not fromObject.
1368	 */
1369	private function getNextFocusManagerComponent2(
1370                            backward:Boolean = false,
1371                            fromObject:DisplayObject = null,
1372                            fromIndex:int = FROM_INDEX_UNSPECIFIED):FocusInfo
1373
1374    {
1375        if (focusableObjects.length == 0)
1376            return null;
1377
1378        // I think we'll have time to do this here instead of at creation time
1379        // this makes and orders the focusableCandidates array
1380        if (calculateCandidates)
1381        {
1382            sortFocusableObjects();
1383            calculateCandidates = false;
1384        }
1385
1386        // trace("focus was at " + o);
1387        // trace("focusableObjects " + focusableObjects.length);
1388        var i:int = fromIndex;
1389        if (fromIndex == FROM_INDEX_UNSPECIFIED)
1390        {
1391	        // if there is no passed in object, then get the object that has the focus
1392    	    var o:DisplayObject = fromObject;
1393        	if (!o)
1394        		o = form.systemManager.stage.focus;
1395
1396	        o = DisplayObject(findFocusManagerComponent2(InteractiveObject(o)));
1397
1398	        var g:String = "";
1399	        if (o is IFocusManagerGroup)
1400	        {
1401	            var tg:IFocusManagerGroup = IFocusManagerGroup(o);
1402	            g = tg.groupName;
1403	        }
1404	        i = getIndexOfFocusedObject(o);
1405        }
1406
1407        // trace(" starting at " + i);
1408        var bSearchAll:Boolean = false;
1409        var start:int = i;
1410        if (i == -1) // we didn't find it
1411        {
1412            if (backward)
1413                i = focusableCandidates.length;
1414            bSearchAll = true;
1415            // trace("search all " + i);
1416        }
1417
1418        var j:int = getIndexOfNextObject(i, backward, bSearchAll, g);
1419
1420        // if we wrapped around, get if we have a parent we should pass
1421        // focus to.
1422        var wrapped:Boolean = false;
1423        if (backward)
1424        {
1425        	if (j >= i)
1426        		wrapped = true;
1427        }
1428        else if (j <= i)
1429      		wrapped = true;
1430
1431		var focusInfo:FocusInfo = new FocusInfo();
1432
1433		focusInfo.displayObject = findFocusManagerComponent2(focusableCandidates[j]);
1434		focusInfo.wrapped = wrapped;
1435
1436        return focusInfo;
1437    }
1438
1439
1440    /**
1441     *  @private
1442     */
1443    private function getTopLevelFocusTarget(o:InteractiveObject):InteractiveObject
1444    {
1445        while (o != InteractiveObject(form))
1446        {
1447            if (o is IFocusManagerComponent &&
1448                IFocusManagerComponent(o).focusEnabled &&
1449                IFocusManagerComponent(o).mouseFocusEnabled &&
1450                (o is IUIComponent ? IUIComponent(o).enabled : true))
1451                return o;
1452
1453			// if we cross a boundry into a bridged application, then return null so
1454			// the target is only processed at the lowest level
1455			if (o.parent is ISWFLoader)
1456			{
1457				if (ISWFLoader(o.parent).swfBridge)
1458					return null;
1459			}
1460            o = o.parent;
1461
1462            if (o == null)
1463                break;
1464        }
1465
1466        return null;
1467    }
1468
1469    /**
1470     *  Returns a String representation of the component hosting the FocusManager object,
1471     *  with the String <code>".focusManager"</code> appended to the end of the String.
1472     *
1473     *  @return Returns a String representation of the component hosting the FocusManager object,
1474     *  with the String <code>".focusManager"</code> appended to the end of the String.
1475     */
1476    public function toString():String
1477    {
1478        return Object(form).toString() + ".focusManager";
1479    }
1480
1481    //--------------------------------------------------------------------------
1482    //
1483    //  Event handlers
1484    //
1485    //--------------------------------------------------------------------------
1486
1487    /**
1488     *  @private
1489     *  Listen for children being added
1490     *  and see if they are focus candidates.
1491     */
1492    private function addedHandler(event:Event):void
1493    {
1494        var target:DisplayObject = DisplayObject(event.target);
1495
1496        // trace("FM: addedHandler: got added for " + target);
1497
1498        // if it is truly parented, add it, otherwise it will get added when the top of the tree
1499        // gets parented.
1500        if (target.stage)
1501        {
1502            // trace("FM: addedHandler: adding focusables");
1503            addFocusables(DisplayObject(event.target));
1504        }
1505    }
1506
1507    /**
1508     *  @private
1509     *  Listen for children being removed.
1510     */
1511    private function removedHandler(event:Event):void
1512    {
1513        var i:int;
1514        var o:DisplayObject = DisplayObject(event.target);
1515
1516        // trace("FM got added for " + event.target);
1517
1518        if (o is IFocusManagerComponent)
1519        {
1520            for (i = 0; i < focusableObjects.length; i++)
1521            {
1522                if (o == focusableObjects[i])
1523                {
1524                    if (o == _lastFocus)
1525                    {
1526                        _lastFocus.drawFocus(false);
1527                        _lastFocus = null;
1528                    }
1529                    // trace("FM removed " + o);
1530                    o.removeEventListener("tabEnabledChange", tabEnabledChangeHandler);
1531                    o.removeEventListener("tabIndexChange", tabIndexChangeHandler);
1532                    focusableObjects.splice(i, 1);
1533                    focusableCandidates = [];
1534                    calculateCandidates = true;
1535                    break;
1536                }
1537            }
1538        }
1539        removeFocusables(o, false);
1540    }
1541
1542	/**
1543	 * After the form is added to the stage, if there are no focusable objects,
1544	 * add the form and its children to the list of focuable objects because
1545	 * this application may have been loaded before the
1546	 * top-level system manager was added to the stage.
1547	 */
1548	private function addedToStageHandler(event:Event):void
1549	{
1550		_form.removeEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
1551		if (focusableObjects.length == 0)
1552		{
1553			addFocusables(DisplayObject(_form));
1554			calculateCandidates = true;
1555		}
1556	}
1557
1558    /**
1559     *  @private
1560     */
1561    private function removeFocusables(o:DisplayObject, dontRemoveTabChildrenHandler:Boolean):void
1562    {
1563        var i:int;
1564        if (o is DisplayObjectContainer)
1565        {
1566            if (!dontRemoveTabChildrenHandler)
1567                o.removeEventListener("tabChildrenChange", tabChildrenChangeHandler);
1568
1569            for (i = 0; i < focusableObjects.length; i++)
1570            {
1571                if (isParent(DisplayObjectContainer(o), focusableObjects[i]))
1572                {
1573                    if (focusableObjects[i] == _lastFocus)
1574                    {
1575                        _lastFocus.drawFocus(false);
1576                        _lastFocus = null;
1577                    }
1578                    // trace("FM removed " + focusableObjects[i]);
1579                    focusableObjects[i].removeEventListener(
1580                        "tabEnabledChange", tabEnabledChangeHandler);
1581                    focusableObjects[i].removeEventListener(
1582                        "tabIndexChange", tabIndexChangeHandler);
1583                    focusableObjects.splice(i, 1);
1584                    i = i - 1;  // because increment would skip one
1585
1586                    focusableCandidates = [];
1587                    calculateCandidates = true;
1588                }
1589            }
1590        }
1591    }
1592
1593    /**
1594     *  @private
1595     */
1596    private function showHandler(event:Event):void
1597    {
1598        form.systemManager.activate(form);
1599    }
1600
1601    /**
1602     *  @private
1603     */
1604    private function hideHandler(event:Event):void
1605    {
1606        form.systemManager.deactivate(form);
1607    }
1608
1609    /**
1610     *  @private
1611     */
1612    private function creationCompleteHandler(event:FlexEvent):void
1613    {
1614        var o:DisplayObject = DisplayObject(form);
1615        if (o.parent && o.visible && !activated)
1616            form.systemManager.activate(form);
1617    }
1618
1619    /**
1620     *  @private
1621     *  Add or remove if tabbing properties change.
1622     */
1623    private function tabIndexChangeHandler(event:Event):void
1624    {
1625        calculateCandidates = true;
1626    }
1627
1628    /**
1629     *  @private
1630     *  Add or remove if tabbing properties change.
1631     */
1632    private function tabEnabledChangeHandler(event:Event):void
1633    {
1634        calculateCandidates = true;
1635
1636        var o:InteractiveObject = InteractiveObject(event.target);
1637        var n:int = focusableObjects.length;
1638        for (var i:int = 0; i < n; i++)
1639        {
1640            if (focusableObjects[i] == o)
1641                break;
1642        }
1643        if (o.tabEnabled)
1644        {
1645            if (i == n && isTabVisible(o))
1646            {
1647                // trace("FM tpc added " + o);
1648                // add it if were not already
1649               	if (focusableObjects.indexOf(o) == -1)
1650	                focusableObjects.push(o);
1651            }
1652        }
1653        else
1654        {
1655            // remove it
1656            if (i < n)
1657            {
1658                // trace("FM tpc removed " + o);
1659                focusableObjects.splice(i, 1);
1660            }
1661        }
1662    }
1663
1664    /**
1665     *  @private
1666     *  Add or remove if tabbing properties change.
1667     */
1668    private function tabChildrenChangeHandler(event:Event):void
1669    {
1670        if (event.target != event.currentTarget)
1671            return;
1672
1673        calculateCandidates = true;
1674
1675        var o:DisplayObjectContainer = DisplayObjectContainer(event.target);
1676        if (o.tabChildren)
1677        {
1678            addFocusables(o, true);
1679        }
1680        else
1681        {
1682            removeFocusables(o, true);
1683        }
1684    }
1685
1686    /**
1687     *  @private
1688     *  This gets called when mouse clicks on a focusable object.
1689     *  We block player behavior
1690     */
1691    private function mouseFocusChangeHandler(event:FocusEvent):void
1692    {
1693        // trace("FocusManager: mouseFocusChangeHandler  in  = " + this._form.systemManager.loaderInfo.url);
1694    	// trace("FocusManager: mouseFocusChangeHandler " + event);
1695
1696        // If relatedObject is null because we don't have access to the
1697        // object getting focus then allow the Player to set focus
1698        // to the object. The isRelatedObjectInaccessible property is
1699        // Player 10 only so we have to test if it is available. We
1700        // will only see isRelatedObjectInaccessible if we are a version "10" swf
1701        // (-target-player=10). Version "9" swfs will not see the property
1702        // even if running in Player 10.
1703        if (event.relatedObject == null &&
1704            "isRelatedObjectInaccessible" in event &&
1705            event["isRelatedObjectInaccessible"] == true)
1706        {
1707            // lost focus to a control in different sandbox.
1708            return;
1709        }
1710
1711        if (event.relatedObject is TextField)
1712        {
1713            var tf:TextField = event.relatedObject as TextField;
1714            if (tf.type == "input" || tf.selectable)
1715            {
1716                return; // pass it on
1717            }
1718        }
1719
1720        event.preventDefault();
1721    }
1722
1723    /**
1724     *  @private
1725     *  This gets called when the tab key is hit.
1726     */
1727    private function keyFocusChangeHandler(event:FocusEvent):void
1728    {
1729        // trace("keyFocusChangeHandler handled by " + this);
1730    	// trace("keyFocusChangeHandler event = " + event);
1731
1732    	var sm:ISystemManager = form.systemManager;
1733
1734		// if the target is in a bridged application, let it handle the click.
1735   		if (sm.isDisplayObjectInABridgedApplication(DisplayObject(event.target)))
1736   			return;
1737
1738        showFocusIndicator = true;
1739		focusChanged = false;
1740        if (event.keyCode == Keyboard.TAB && !event.isDefaultPrevented())
1741        {
1742            if (browserFocusComponent)
1743            {
1744                if (browserFocusComponent.tabIndex == LARGE_TAB_INDEX)
1745                    browserFocusComponent.tabIndex = -1;
1746
1747                browserFocusComponent = null;
1748
1749				if (SystemManager(form.systemManager).useSWFBridge())
1750				{
1751					// out of children, pass focus to parent
1752					moveFocusToParent(event.shiftKey);
1753
1754					if (focusChanged)
1755		            	event.preventDefault();
1756				}
1757
1758                return;
1759            }
1760
1761            // trace("tabHandled by " + this);
1762            setFocusToNextObject(event);
1763
1764			if (focusChanged)
1765            	event.preventDefault();
1766        }
1767    }
1768
1769    /**
1770     *  @private
1771     *  Watch for TAB keys.
1772     */
1773    private function keyDownHandler(event:KeyboardEvent):void
1774    {
1775        // trace("onKeyDown handled by " + this);
1776    	// trace("onKeyDown event = " + event);
1777		// if the target is in a bridged application, let it handle the click.
1778		var sm:ISystemManager = form.systemManager;
1779   		if (sm.isDisplayObjectInABridgedApplication(DisplayObject(event.target)))
1780   			return;
1781
1782        if (sm is SystemManager)
1783            SystemManager(sm).idleCounter = 0;
1784
1785        if (event.keyCode == Keyboard.TAB)
1786        {
1787            lastAction = "KEY";
1788
1789            // I think we'll have time to do this here instead of at creation time
1790            // this makes and orders the focusableCandidates array
1791            if (calculateCandidates)
1792            {
1793                sortFocusableObjects();
1794                calculateCandidates = false;
1795            }
1796        }
1797
1798        if (browserMode)
1799        {
1800            if (event.keyCode == Keyboard.TAB && focusableCandidates.length > 0)
1801            {
1802                // get the object that has the focus
1803                var o:DisplayObject = fauxFocus;
1804				if (!o)
1805				{
1806					o = form.systemManager.stage.focus;
1807				}
1808
1809                // trace("focus was at " + o);
1810                // trace("focusableObjects " + focusableObjects.length);
1811                o = DisplayObject(findFocusManagerComponent2(InteractiveObject(o)));
1812                var g:String = "";
1813                if (o is IFocusManagerGroup)
1814                {
1815                    var tg:IFocusManagerGroup = IFocusManagerGroup(o);
1816                    g = tg.groupName;
1817                }
1818
1819                var i:int = getIndexOfFocusedObject(o);
1820                var j:int = getIndexOfNextObject(i, event.shiftKey, false, g);
1821                if (event.shiftKey)
1822                {
1823                    if (j >= i)
1824                    {
1825                        // we wrapped so let browser have it
1826                        browserFocusComponent = getBrowserFocusComponent(event.shiftKey);
1827                        if (browserFocusComponent.tabIndex == -1)
1828                            browserFocusComponent.tabIndex = 0;
1829                    }
1830                }
1831                else
1832                {
1833                    if (j <= i)
1834                    {
1835                        // we wrapped so let browser have it
1836                        browserFocusComponent = getBrowserFocusComponent(event.shiftKey);
1837                        if (browserFocusComponent.tabIndex == -1)
1838                            browserFocusComponent.tabIndex = LARGE_TAB_INDEX;
1839                    }
1840                }
1841            }
1842        }
1843    }
1844
1845    /**
1846     *  @private
1847     *  Watch for ENTER key.
1848     */
1849    private function defaultButtonKeyHandler(event:KeyboardEvent):void
1850    {
1851        var sm:ISystemManager = form.systemManager;
1852        if (sm.isDisplayObjectInABridgedApplication(DisplayObject(event.target)))
1853            return;
1854
1855        if (defaultButtonEnabled && event.keyCode == Keyboard.ENTER &&
1856            defaultButton && defButton.enabled)
1857        {
1858            sendDefaultButtonEvent();
1859        }
1860    }
1861
1862    /**
1863     *  @private
1864     *  This gets called when the focus changes due to a mouse click.
1865     *
1866     *  Note: If the focus is changing to a TextField, we don't call
1867     *  setFocus() on it because the player handles it;
1868     *  calling setFocus() on a TextField which has scrollable text
1869     *  causes the text to autoscroll to the end, making the
1870     *  mouse click set the insertion point in the wrong place.
1871     */
1872    private function mouseDownHandler(event:MouseEvent):void
1873    {
1874        // trace("FocusManager mouseDownHandler in  = " + this._form.systemManager.loaderInfo.url);
1875        // trace("FocusManager mouseDownHandler target " + event.target);
1876
1877        if (event.isDefaultPrevented())
1878            return;
1879
1880		// if the target is in a bridged application, let it handle the click.
1881		var sm:ISystemManager = form.systemManager;
1882        var o:DisplayObject = getTopLevelFocusTarget(
1883            InteractiveObject(event.target));
1884
1885        if (!o)
1886            return;
1887
1888        showFocusIndicator = false;
1889
1890        // trace("FocusManager mouseDownHandler on " + o);
1891
1892        // Make sure the containing component gets notified.
1893        // As the note above says, we don't set focus to a TextField ever
1894        // because the player already did and took care of where
1895        // the insertion point is, and we also don't call setfocus
1896        // on a component that last the last focused object unless
1897        // the last action was just to activate the player and didn't
1898        // involve tabbing or clicking on a component
1899        if ((o != _lastFocus || lastAction == "ACTIVATE") && !(o is TextField))
1900            setFocus(IFocusManagerComponent(o));
1901		else if (_lastFocus)
1902			// trace("FM: skipped setting focus to " + _lastFocus);
1903
1904        // if in a sandbox, create a focus-in event and dispatch.
1905		if (!_lastFocus && o is IEventDispatcher &&	SystemManager(form.systemManager).useSWFBridge())
1906       		IEventDispatcher(o).dispatchEvent(new FocusEvent(FocusEvent.FOCUS_IN));
1907
1908        lastAction = "MOUSEDOWN";
1909
1910		dispatchActivatedFocusManagerEvent(null);
1911
1912		lastActiveFocusManager = this;
1913
1914    }
1915
1916
1917	/**
1918	 * @private
1919	 *
1920	 * A request across a bridge from another FocusManager to change the
1921	 * focus.
1922	 */
1923    private function focusRequestMoveHandler(event:Event):void
1924    {
1925    	// trace("focusRequestHandler in  = " + this._form.systemManager.loaderInfo.url);
1926
1927		// ignore messages we send to ourselves.
1928		if (event is SWFBridgeRequest)
1929		{
1930			// trace("ignored focus in " + this._form.systemManager.loaderInfo.url);
1931			return;
1932		}
1933
1934		focusSetLocally = false;
1935
1936    	var request:SWFBridgeRequest = SWFBridgeRequest.marshal(event);
1937		if (request.data == FocusRequestDirection.TOP || request.data == FocusRequestDirection.BOTTOM)
1938		{
1939			// move focus to the top or bottom child. If there are no children then
1940			// send focus back up to the parent.
1941			if (focusableObjects.length == 0)
1942			{
1943				// trace("focusRequestMoveHandler: no focusable objects, setting focus back to parent");
1944				moveFocusToParent(request.data == FocusRequestDirection.TOP ? false : true);
1945				event["data"] = focusChanged;
1946				return;
1947			}
1948
1949			if (request.data == FocusRequestDirection.TOP)
1950			{
1951				setFocusToTop();
1952			}
1953			else
1954			{
1955				setFocusToBottom();
1956			}
1957
1958			event["data"] = focusChanged;
1959		}
1960		else
1961		{
1962			// move forward or backward
1963			var startingPosition:DisplayObject = DisplayObject(_form.systemManager.
1964										          swfBridgeGroup.getChildBridgeProvider(IEventDispatcher(event.target)));
1965			moveFocus(request.data as String, startingPosition);
1966			event["data"] = focusChanged;
1967	  	}
1968
1969		if (focusSetLocally)
1970		{
1971            dispatchActivatedFocusManagerEvent(null);
1972            lastActiveFocusManager = this;
1973		}
1974    }
1975
1976    private function focusRequestActivateHandler(event:Event):void
1977	{
1978		// trace("FM focusRequestActivateHandler");
1979		skipBridge = IEventDispatcher(event.target);
1980		activate();
1981		skipBridge = null;
1982	}
1983
1984    private function focusRequestDeactivateHandler(event:Event):void
1985	{
1986		// trace("FM focusRequestDeactivateHandler");
1987        skipBridge = IEventDispatcher(event.target);
1988		deactivate();
1989        skipBridge = null;
1990	}
1991
1992    private function bridgeEventActivateHandler(event:Event):void
1993	{
1994		// ignore message to self
1995		if (event is SWFBridgeEvent)
1996			return;
1997
1998		//trace("FM bridgeEventActivateHandler for " + form.systemManager.loaderInfo.url);
1999
2000		// clear last focus if we aren't active.
2001		lastActiveFocusManager = null;
2002		_lastFocus = null;
2003
2004		dispatchActivatedFocusManagerEvent(IEventDispatcher(event.target));
2005	}
2006
2007    /**
2008     *  @private
2009     *
2010     *  A request across a bridge from another FocusManager to change the
2011     *  value of the setShowFocusIndicator property.
2012     */
2013    private function setShowFocusIndicatorRequestHandler(event:Event):void
2014    {
2015        // ignore messages we send to ourselves.
2016        if (event is SWFBridgeRequest)
2017            return;
2018
2019        var request:SWFBridgeRequest = SWFBridgeRequest.marshal(event);
2020        _showFocusIndicator = request.data;
2021
2022        // relay the message to parent and children
2023        dispatchSetShowFocusIndicatorRequest(_showFocusIndicator, IEventDispatcher(event.target));
2024    }
2025
2026	/**
2027	 * This is called on the top-level focus manager and the parent focus
2028	 * manager for each new bridge of focusable content created.
2029	 * When the parent focus manager of the new focusable content is
2030	 * called, focusable content will become part of the tab order.
2031	 * When the top-level focus manager is called the bridge becomes
2032	 * one of the focus managers managed by the top-level focus manager.
2033	 */
2034	public function addSWFBridge(bridge:IEventDispatcher, owner:DisplayObject):void
2035	{
2036//    	trace("FocusManager.addFocusManagerBridge: in  = " + this._form.systemManager.loaderInfo.url);
2037        if (!owner)
2038            return;
2039
2040		var sm:ISystemManager = _form.systemManager;
2041       	if (focusableObjects.indexOf(owner) == -1)
2042		{
2043			focusableObjects.push(owner);
2044    	    calculateCandidates = true;
2045   		}
2046
2047		// listen for move requests from the bridge.
2048        swfBridgeGroup.addChildBridge(bridge, ISWFBridgeProvider(owner));
2049
2050   		bridge.addEventListener(SWFBridgeRequest.MOVE_FOCUS_REQUEST, focusRequestMoveHandler);
2051        bridge.addEventListener(SWFBridgeRequest.SET_SHOW_FOCUS_INDICATOR_REQUEST,
2052                                setShowFocusIndicatorRequestHandler);
2053  		bridge.addEventListener(SWFBridgeEvent.BRIDGE_FOCUS_MANAGER_ACTIVATE,
2054   				    		    bridgeEventActivateHandler);
2055	}
2056
2057	/**
2058	 * @inheritdoc
2059	 */
2060	public function removeSWFBridge(bridge:IEventDispatcher):void
2061	{
2062		var sm:ISystemManager = _form.systemManager;
2063		var displayObject:DisplayObject = DisplayObject(swfBridgeGroup.getChildBridgeProvider(bridge));
2064		if (displayObject)
2065		{
2066			var index:int = focusableObjects.indexOf(displayObject);
2067           	if (index != -1)
2068			{
2069				focusableObjects.splice(index, 1);
2070        	    calculateCandidates = true;
2071
2072   			}
2073
2074		}
2075	    else
2076	    	throw new Error();		// should never get here.
2077
2078   		bridge.removeEventListener(SWFBridgeRequest.MOVE_FOCUS_REQUEST, focusRequestMoveHandler);
2079        bridge.removeEventListener(SWFBridgeRequest.SET_SHOW_FOCUS_INDICATOR_REQUEST,
2080                                    setShowFocusIndicatorRequestHandler);
2081  		bridge.removeEventListener(SWFBridgeEvent.BRIDGE_FOCUS_MANAGER_ACTIVATE,
2082   					    		    bridgeEventActivateHandler);
2083        swfBridgeGroup.removeChildBridge(bridge);
2084	}
2085
2086	/**
2087	 */
2088	private function removeFromParentBridge(event:Event):void
2089	{
2090		// add ourselves to our parent focus manager if this is a bridged
2091		// application not a dialog or other popup.
2092		var sm:ISystemManager = form.systemManager;
2093		if (sm.useSWFBridge())
2094		{
2095			sm.removeEventListener(SWFBridgeEvent.BRIDGE_APPLICATION_UNLOADING, removeFromParentBridge);
2096
2097			// have the child listen to move requests from the parent.
2098			var bridge:IEventDispatcher = swfBridgeGroup.parentBridge;
2099		    if (bridge)
2100	    	{
2101	       		bridge.removeEventListener(SWFBridgeRequest.MOVE_FOCUS_REQUEST, focusRequestMoveHandler);
2102                bridge.removeEventListener(SWFBridgeRequest.SET_SHOW_FOCUS_INDICATOR_REQUEST,
2103                                           setShowFocusIndicatorRequestHandler);
2104	       	}
2105
2106	   		// add listener activate/deactivate requests
2107	   		if (bridge && !(form.systemManager is SystemManagerProxy))
2108	   		{
2109	   			bridge.removeEventListener(SWFBridgeRequest.ACTIVATE_FOCUS_REQUEST, focusRequestActivateHandler);
2110	   			bridge.removeEventListener(SWFBridgeRequest.DEACTIVATE_FOCUS_REQUEST, focusRequestDeactivateHandler);
2111			   	bridge.removeEventListener(SWFBridgeEvent.BRIDGE_FOCUS_MANAGER_ACTIVATE,
2112			   				    		bridgeEventActivateHandler);
2113	   		}
2114		}
2115	}
2116
2117	/**
2118	 *  @private
2119	 *
2120	 *  Send a message to the parent to move focus a component in the parent.
2121	 *
2122	 *  @param shiftKey - if true move focus to a component
2123	 *
2124	 *  @return true if focus moved to parent, false otherwise.
2125	 */
2126    private function moveFocusToParent(shiftKey:Boolean):Boolean
2127    {
2128    	// trace("pass focus from " + this.form.systemManager.loaderInfo.url + " to parent ");
2129		var request:SWFBridgeRequest = new SWFBridgeRequest(SWFBridgeRequest.MOVE_FOCUS_REQUEST,
2130													false, true, null,
2131													shiftKey ? FocusRequestDirection.BACKWARD :
2132													  		   FocusRequestDirection.FORWARD);
2133		var sandboxBridge:IEventDispatcher = _form.systemManager.swfBridgeGroup.parentBridge;
2134
2135		// the handler will set the data property to whether focus changed
2136		sandboxBridge.dispatchEvent(request);
2137		focusChanged = request.data;
2138
2139		return focusChanged;
2140    }
2141
2142    /**
2143     *  Get the bridge to the parent focus manager.
2144     *
2145     *  @return parent bridge or null if there is no parent bridge.
2146     */
2147    private function getParentBridge():IEventDispatcher
2148    {
2149    	if (swfBridgeGroup)
2150    		return swfBridgeGroup.parentBridge;
2151
2152		return null;
2153    }
2154
2155
2156    /**
2157     *  @private
2158     *
2159     *  Send a request for all other focus managers to update
2160     *  their ShowFocusIndicator property.
2161     */
2162    private function dispatchSetShowFocusIndicatorRequest(value:Boolean, skip:IEventDispatcher):void
2163    {
2164        var request:SWFBridgeRequest = new SWFBridgeRequest(
2165                                            SWFBridgeRequest.SET_SHOW_FOCUS_INDICATOR_REQUEST,
2166                                            false,
2167                                            false,
2168                                            null,   // bridge is set before it is dispatched
2169                                            value);
2170        dispatchEventFromSWFBridges(request, skip);
2171    }
2172
2173	/**
2174	 *  @private
2175	 *
2176	 *  Broadcast an ACTIVATED_FOCUS_MANAGER message.
2177	 *
2178	 *  @param eObj if a SandboxBridgeEvent, then propagate the message,
2179	 * 			   if null, start a new message.
2180	 */
2181	private function dispatchActivatedFocusManagerEvent(skip:IEventDispatcher = null):void
2182	{
2183
2184		// Don't send the ACTIVATED_FOCUS_MANAGER message if we are already
2185		// active.
2186       	if (lastActiveFocusManager == this)
2187       	{
2188       		// trace("FM: dispatchActivatedFocusManagerEvent already active, skipping messages");
2189       		return;		// already active
2190       	}
2191
2192    	var event:SWFBridgeEvent = new SWFBridgeEvent(SWFBridgeEvent.BRIDGE_FOCUS_MANAGER_ACTIVATE);
2193        dispatchEventFromSWFBridges(event, skip);
2194	}
2195
2196	/**
2197     *  A Focus Manager has its own set of child bridges that may be different from the child
2198     *  bridges of its System Manager if the Focus Manager is managing a pop up. In the case of
2199     *  a pop up don't send messages to the SM parent bridge because that will be the form. But
2200     *  do send the messages to the bridges in bridgeFocusManagers dictionary.
2201     */
2202    private function dispatchEventFromSWFBridges(event:Event, skip:IEventDispatcher = null):void
2203    {
2204
2205
2206        var clone:Event;
2207        // trace(">>dispatchEventFromSWFBridges", this, event.type);
2208        var sm:ISystemManager = form.systemManager;
2209
2210        if (!popup)
2211        {
2212            var parentBridge:IEventDispatcher = swfBridgeGroup.parentBridge;
2213
2214            if (parentBridge && parentBridge != skip)
2215            {
2216                // Ensure the requestor property has the correct bridge.
2217                clone = event.clone();
2218                if (clone is SWFBridgeRequest)
2219                    SWFBridgeRequest(clone).requestor = parentBridge;
2220
2221                parentBridge.dispatchEvent(clone);
2222            }
2223        }
2224
2225        var children:Array = swfBridgeGroup.getChildBridges();
2226        for (var i:int = 0; i < children.length; i++)
2227        {
2228            if (children[i] != skip)
2229            {
2230                // trace("send to child", i, event.type);
2231                clone = event.clone();
2232
2233                // Ensure the requestor property has the correct bridge.
2234                if (clone is SWFBridgeRequest)
2235                    SWFBridgeRequest(clone).requestor = IEventDispatcher(children[i]);
2236
2237                IEventDispatcher(children[i]).dispatchEvent(clone);
2238            }
2239        }
2240
2241        // trace("<<dispatchEventFromSWFBridges", this, event.type);
2242    }
2243
2244	private function getBrowserFocusComponent(shiftKey:Boolean):InteractiveObject
2245	{
2246    	var focusComponent:InteractiveObject = form.systemManager.stage.focus;
2247
2248		// if the focus is null it means focus is in an application we
2249		// don't have access to. Use either the last object or the first
2250		// object in this focus manager's list.
2251		if (!focusComponent)
2252		{
2253			var index:int = shiftKey ? 0 : focusableCandidates.length - 1;
2254			focusComponent = focusableCandidates[index];
2255		}
2256
2257		return focusComponent;
2258	}
2259}
2260
2261}
2262
2263import flash.display.DisplayObject;
2264
2265/**
2266 * @private
2267 *
2268 *  Plain old class to return multiple items of info about the potential
2269 *  change in focus.
2270 */
2271class FocusInfo
2272{
2273	public var displayObject:DisplayObject;	// object to get focus
2274	public var wrapped:Boolean;				// true if focus wrapped around
2275}
2276