////////////////////////////////////////////////////////////////////////////////
//
//  ADOBE SYSTEMS INCORPORATED
//  Copyright 2003-2007 Adobe Systems Incorporated
//  All Rights Reserved.
//
//  NOTICE: Adobe permits you to use, modify, and distribute this file
//  in accordance with the terms of the license agreement accompanying it.
//
////////////////////////////////////////////////////////////////////////////////

package mx.managers
{

import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.display.InteractiveObject;
import flash.display.Sprite;
import flash.display.Stage;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.FocusEvent;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.system.Capabilities;
import flash.system.IME;
import flash.text.TextField;
import flash.ui.Keyboard;

import mx.core.FlexSprite;
import mx.core.IButton;
import mx.core.IChildList;
import mx.core.IIMESupport;
import mx.core.IRawChildrenContainer;
import mx.core.ISWFLoader;
import mx.core.IToggleButton;
import mx.core.IUIComponent;
import mx.core.IVisualElement;
import mx.core.mx_internal;
import mx.events.FlexEvent;

use namespace mx_internal;

/**
 *  The FocusManager class manages the focus on components in response to mouse
 *  activity or keyboard activity (Tab key).  There can be several FocusManager
 *  instances in an application.  Each FocusManager instance 
 *  is responsible for a set of components that comprise a "tab loop".  If you
 *  hit Tab enough times, focus traverses through a set of components and
 *  eventually get back to the first component that had focus.  That is a "tab loop"
 *  and a FocusManager instance manages that loop.  If there are popup windows
 *  with their own set of components in a "tab loop" those popup windows will have
 *  their own FocusManager instances.  The main application always has a
 *  FocusManager instance.
 *
 *  <p>The FocusManager manages focus from the "component level".
 *  In Flex, a UITextField in a component is the only way to allow keyboard entry
 *  of text. To the Flash Player or AIR, that UITextField has focus. However, from the 
 *  FocusManager's perspective the component that parents the UITextField has focus.
 *  Thus there is a distinction between component-level focus and player-level focus.
 *  Application developers generally only have to deal with component-level focus while
 *  component developers must understand player-level focus.</p>
 *
 *  <p>All components that can be managed by the FocusManager must implement
 *  mx.managers.IFocusManagerComponent, whereas objects managed by player-level focus do not.</p>  
 *
 *  <p>The FocusManager also managers the concept of a defaultButton, which is
 *  the Button on a form that dispatches a click event when the Enter key is pressed
 *  depending on where focus is at that time.</p>
 *  
 *  @langversion 3.0
 *  @playerversion Flash 9
 *  @playerversion AIR 1.1
 *  @productversion Flex 3
 */
public class FocusManager extends EventDispatcher implements IFocusManager
{
    include "../core/Version.as";

	//--------------------------------------------------------------------------
	//
	//  Class constants
	//
	//--------------------------------------------------------------------------

	/**
	 * @private
	 * 
	 * Default value of parameter, ignore. 
	 */
	private static const FROM_INDEX_UNSPECIFIED:int = -2;
	
	//--------------------------------------------------------------------------
	//
	//  Class variables
	//
	//--------------------------------------------------------------------------

	/**
	 * @private
	 * 
	 * Place to hook in additional classes
	 */
	public static var mixins:Array;

    //--------------------------------------------------------------------------
    //
    //  Constructor
    //
    //--------------------------------------------------------------------------

    /**
     *  Constructor.
     *
     *  <p>A FocusManager manages the focus within the children of an IFocusManagerContainer.
     *  It installs itself in the IFocusManagerContainer during execution
     *  of the constructor.</p>
     *
     *  @param container An IFocusManagerContainer that hosts the FocusManager.
     *
     *  @param popup If <code>true</code>, indicates that the container
     *  is a popup component and not the main application.
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public function FocusManager(container:IFocusManagerContainer, popup:Boolean = false)
    {
        super();

		this.popup = popup;

        IMEEnabled = true;
        browserMode = Capabilities.playerType == "ActiveX" && !popup;
        desktopMode = Capabilities.playerType == "Desktop" && !popup;
        // Flash main windows come up activated, AIR main windows don't
        windowActivated = !desktopMode;
    
        container.focusManager = this; // this property name is reserved in the parent

        // trace("FocusManager constructor " + container + ".focusManager");
        
        _form = container;
        
        focusableObjects = [];

        focusPane = new FlexSprite();
        focusPane.name = "focusPane";

        addFocusables(DisplayObject(container));
        
        // Listen to the stage so we know when the root application is loaded.
        container.addEventListener(Event.ADDED, addedHandler);
        container.addEventListener(Event.REMOVED, removedHandler);
        container.addEventListener(FlexEvent.SHOW, showHandler);
        container.addEventListener(FlexEvent.HIDE, hideHandler);
        container.addEventListener(FlexEvent.HIDE, childHideHandler, true);
        container.addEventListener("_navigationChange_",viewHideHandler, true);
        
        //special case application and window
        if (container.systemManager is SystemManager)
        {
            // special case application.  It shouldn't need to be made
            // active and because we defer appCreationComplete, this 
            // would steal focus back from any popups created during
            // instantiation
            if (container != SystemManager(container.systemManager).application)
                container.addEventListener(FlexEvent.CREATION_COMPLETE,
                                       creationCompleteHandler);
        }
        
		if (mixins)
		{
			var n:int = mixins.length;
			for (var i:int = 0; i < n; i++)
			{
				new mixins[i](this);
			}
		}

        // Make sure the SystemManager is running so it can tell us about
        // mouse clicks and stage size changes.
		try
		{
			var awm:IActiveWindowManager = 
				IActiveWindowManager(container.systemManager.getImplementation("mx.managers::IActiveWindowManager"));
			if (awm)
        		awm.addFocusManager(container); // build a message that does the equal

            if (hasEventListener("initialize"))
		    	dispatchEvent(new Event("initialize"));

		}
		catch (e:Error)
		{
			// ignore null pointer errors caused by container using a 
			// systemManager from another sandbox.
		}
    }

    //--------------------------------------------------------------------------
    //
    //  Variables
    //
    //--------------------------------------------------------------------------

    private var LARGE_TAB_INDEX:int = 99999;

    mx_internal var calculateCandidates:Boolean = true;

	/**
	 * @private
	 * 
	 * True if this focus manager is a popup, false if it is a main application.
	 * 
	 */
	mx_internal var popup:Boolean;

    /**
     * @private
     * 
     * True if this focus manager will try to enable/disable the IME based on
     * whether the focused control uses IME.  Leaving this as a backdoor just in case.
     * 
     */
    mx_internal var IMEEnabled:Boolean;

    /**
     *  @private
     *  We track whether we've been last activated or saw a TAB
     *  This is used in browser tab management
     */
    mx_internal var lastAction:String;

    /**
     *  @private
     *  Tab management changes based on whether were in a browser or not
     *  This value is also affected by whether you are a modal dialog or not
     */
    public var browserMode:Boolean;

    /**
     *  @private
     *  Activation changes depending on whether we're running in AIR or not
     */
    public var desktopMode:Boolean;

    /**
     *  @private
     *  Tab management changes based on whether were in a browser or not
     *  If non-null, this is the object that will
     *  lose focus to the browser
     */
    private var browserFocusComponent:InteractiveObject;

    /**
     *  @private
     *  Total set of all objects that can receive focus
     *  but might be disabled or invisible.
     */
    mx_internal var focusableObjects:Array;
    
    /**
     *  @private
     *  Filtered set of objects that can receive focus right now.
     */
    private var focusableCandidates:Array;

    /**
     *  @private
     */
    private var activated:Boolean;
    /**
     *  @private
     */
    private var windowActivated:Boolean;
    
    /**
     * 	@private
     * 
     * 	true if focus was changed to one of focusable objects. False if focus passed to 
     * 	the browser.
     */
	mx_internal var focusChanged:Boolean;

    /**
	 * 	@private
	 * 
	 * 	if non-null, the location to move focus from instead of the object 
	 *  that has focus in the stage.
	 */
	mx_internal var fauxFocus:DisplayObject;
	 
	 
	//--------------------------------------------------------------------------
    //
    //  Properties
    //
    //--------------------------------------------------------------------------

    //----------------------------------
    //  showFocusIndicator
    //----------------------------------

    /**
     *  @private
     *  Storage for the showFocusIndicator property.
     */
    mx_internal var _showFocusIndicator:Boolean = false;
    
    /**
     *  @inheritDoc
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public function get showFocusIndicator():Boolean
    {
        return _showFocusIndicator;
    }
    
    /**
     *  @private
     */
    public function set showFocusIndicator(value:Boolean):void
    {
        var changed:Boolean = _showFocusIndicator != value;
        // trace("FM " + this + " showFocusIndicator = " + value);
        _showFocusIndicator = value;

        if (hasEventListener("showFocusIndicator"))
            dispatchEvent(new Event("showFocusIndicator"));
    }

    //----------------------------------
    //  defaultButton
    //----------------------------------

    /**
     *  @private
     *  The current default button.
     */
    private var defButton:IButton;

    /**
     *  @private
     */
    private var _defaultButton:IButton;

    /**
     *  @inheritDoc
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public function get defaultButton():IButton
    {
		return _defaultButton;
    }

    /**
     *  @private
     *  We don't type the value as Button for dependency reasons
     */
    public function set defaultButton(value:IButton):void
    {
		var button:IButton = value ? IButton(value) : null;

        if (button != _defaultButton)
        {
            if (_defaultButton)
                _defaultButton.emphasized = false;
            
            if (defButton)  
                defButton.emphasized = false;
            
            _defaultButton = button;
            
            if (defButton != _lastFocus || _lastFocus == _defaultButton)
            {
            defButton = button;
            
            if (button)
                button.emphasized = true;
        }
    }
    }

    //----------------------------------
    //  defaultButtonEnabled
    //----------------------------------

    /**
     *  @private
     *  Storage for the defaultButtonEnabled property.
     */
    private var _defaultButtonEnabled:Boolean = true;

    /**
     *  @inheritDoc
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public function get defaultButtonEnabled():Boolean
    {
        return _defaultButtonEnabled;
    }
    
    /**
     *  @private
     */
    public function set defaultButtonEnabled(value:Boolean):void
    {
        _defaultButtonEnabled = value;
        
        // Synchronize with the new value. We ensure that our 
        // default button is de-emphasized if defaultButtonEnabled
        // is false.
        if (defButton)
            defButton.emphasized = value;
    }
    
    //----------------------------------
    //  focusPane
    //----------------------------------

    /**
     *  @private
     *  Storage for the focusPane property.
     */
    private var _focusPane:Sprite;

    /**
     *  @inheritDoc
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public function get focusPane():Sprite
    {
        return _focusPane;
    }

    /**
     *  @private
     */
    public function set focusPane(value:Sprite):void
    {
        _focusPane = value;
    }

    //----------------------------------
    //  form
    //----------------------------------

    /**
     *  @private
     *  Storage for the form property.
     */
    private var _form:IFocusManagerContainer;
    
    /**
     *  @private
     *  The form is the property where we store the IFocusManagerContainer
     *  that hosts this FocusManager.
     */
    mx_internal function get form():IFocusManagerContainer
    {
        return _form;
    }
    
    /**
     *  @private
     */
    mx_internal function set form (value:IFocusManagerContainer):void
    {
        _form = value;
    }


    //----------------------------------
    //  _lastFocus
    //----------------------------------
    
    /**
     *  @private
     *  the object that last had focus
     */
    private var _lastFocus:IFocusManagerComponent;


	/**
	 * 	@private
	 */
	mx_internal function get lastFocus():IFocusManagerComponent
	{
		return _lastFocus;
	}
	 
	/**
	 * 	@private
	 */
	mx_internal function set lastFocus(value:IFocusManagerComponent):void
	{
		_lastFocus = value;
	}

    //----------------------------------
    //  nextTabIndex
    //----------------------------------

    /**
     *  @inheritDoc
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public function get nextTabIndex():int
    {
        return getMaxTabIndex() + 1;
    }

    /**
     *  Gets the highest tab index currently used in this Focus Manager's form or subform.
     *
     *  @return Highest tab index currently used.
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    private function getMaxTabIndex():int
    {
        var z:Number = 0;

        var n:int = focusableObjects.length;
        for (var i:int = 0; i < n; i++)
        {
            var t:Number = focusableObjects[i].tabIndex;
            if (!isNaN(t))
                z = Math.max(z, t);
        }
        
        return z;
    }

    //--------------------------------------------------------------------------
    //
    //  Methods
    //
    //--------------------------------------------------------------------------

    /**
     *  @inheritDoc
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public function getFocus():IFocusManagerComponent
    {
        var stage:Stage = form.systemManager.stage;
        
        if (!stage)
            return null;
            
        var o:InteractiveObject = stage.focus;
        
        // If a Stage* object (such as StageText or StageWebView) has focus,
        // stage.focus will be set to null. Much of the focus framework is not
        // set up to handle this. So, if stage.focus is null, we return the last
        // IFocusManagerComponent that had focus.  In ADL, focus works slightly
        // different than it does on device when using StageText.  In ADL, when
        // the focus is a StageText component, a TextField whose parent is the 
        // stage is assigned focus.  
        if ((!o && _lastFocus) || (o is TextField && o.parent == stage))
            return _lastFocus;
        
        return findFocusManagerComponent(o);
    }

    /**
     *  @inheritDoc
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public function setFocus(o:IFocusManagerComponent):void
    {
        // trace("FM " + this + " setting focus to " + o);

        o.setFocus();
        
        if (hasEventListener("setFocus"))
    		dispatchEvent(new Event("setFocus"));
        // trace("FM set focus");
    }

    /**
     *  @private
     */
    private function focusInHandler(event:FocusEvent):void
    {
        var target:InteractiveObject = InteractiveObject(event.target);
        // trace("FocusManager focusInHandler in  = " + this._form.systemManager.loaderInfo.url);
        // trace("FM " + this + " focusInHandler " + target);

		// dispatch cancelable FocusIn to see if Marshal Plan mixin wants it
        if (hasEventListener(FocusEvent.FOCUS_IN))
    		if (!dispatchEvent(new FocusEvent(FocusEvent.FOCUS_IN, false, true, target)))
    			return;

        if (isParent(DisplayObjectContainer(form), target))
        {
            if (_defaultButton)
            {
                if (target is IButton && target != _defaultButton 
                    && !(target is IToggleButton))
                    _defaultButton.emphasized = false;
                else if (_defaultButtonEnabled)
                    _defaultButton.emphasized = true;
            }
 
            // trace("FM " + this + " setting last focus " + target);
            _lastFocus = findFocusManagerComponent(InteractiveObject(target));
            
			if (Capabilities.hasIME)
            {
                var usesIME:Boolean;
                if (_lastFocus is IIMESupport)
                {
                    var imeFocus:IIMESupport = IIMESupport(_lastFocus);
                    if (imeFocus.enableIME)
                        usesIME = true;
                }
                if (IMEEnabled)
                    IME.enabled = usesIME;
            }
            
			// handle default button here
			// we can't check for Button because of cross-versioning so
			// for now we just check for an emphasized property
            if (_lastFocus is IButton && !(_lastFocus is IToggleButton))
			{
                defButton = _lastFocus as IButton;
				}
			else
			{
				// restore the default button to be the original one
				if (defButton && defButton != _defaultButton)
					defButton = _defaultButton;
				}
			}
		}

    /**
     *  @private  Useful for debugging
     */
    private function focusOutHandler(event:FocusEvent):void
    {
        var target:InteractiveObject = InteractiveObject(event.target);
        // trace("FocusManager focusOutHandler in  = " + this._form.systemManager.loaderInfo.url);
        // trace("FM " + this + " focusOutHandler " + target);
    }

    /**
     *  @private
     *  restore focus to whoever had it last
     */
    private function activateHandler(event:Event):void
    {
//        var target:InteractiveObject = InteractiveObject(event.target);
        // trace("FM " + this + " activateHandler ", _lastFocus);
		
        // if we were the active FM when we were deactivated
        // and we're not running in AIR, then dispatch the event now
        // otherwise wait for the AIR events to fire
        if (activated && !desktopMode)
        {
            dispatchEvent(new FlexEvent(FlexEvent.FLEX_WINDOW_ACTIVATE));
		    // restore focus if this focus manager had last focus
	        if (_lastFocus && !browserMode)
	    	    _lastFocus.setFocus();
	        lastAction = "ACTIVATE";
        }
    }

    /**
     *  @private  
     *  Dispatch event if we're not running in AIR.  AIR will
     *  dispatch windowDeactivate that we respond to instead
     */
    private function deactivateHandler(event:Event):void
    {
        // var target:InteractiveObject = InteractiveObject(event.target);
        // trace("FM " + this + " deactivateHandler ", _lastFocus);

        // if we are the active FM when we were deactivated
        // and we're not running in AIR, then dispatch the event now
        // otherwise wait for the AIR events to fire
        if (activated && !desktopMode)
        {
            dispatchEvent(new FlexEvent(FlexEvent.FLEX_WINDOW_DEACTIVATE));
        }
    }

    /**
     *  @private
     *  restore focus to whoever had it last
     */
    private function activateWindowHandler(event:Event):void
    {
//        var target:InteractiveObject = InteractiveObject(event.target);
        // trace("FM " + this + " activateWindowHandler ", _lastFocus);
		
        windowActivated = true;

        if (activated)
        {
            dispatchEvent(new FlexEvent(FlexEvent.FLEX_WINDOW_ACTIVATE));
		    // restore focus if this focus manager had last focus
	        if (_lastFocus && !browserMode)
	    	    _lastFocus.setFocus();
	        lastAction = "ACTIVATE";
        }
    }

    /**
     *  @private  
     *  If we're responsible for the focused control, remove focus from it
     *  so it gets the same events as it would if the whole app lost focus
     */
    private function deactivateWindowHandler(event:Event):void
    {
        // var target:InteractiveObject = InteractiveObject(event.target);
        // trace("FM " + this + " deactivateWindowHandler ", _lastFocus);

        windowActivated = false;

        if (activated)
        {
            dispatchEvent(new FlexEvent(FlexEvent.FLEX_WINDOW_DEACTIVATE));
            if (form.systemManager.stage)
                form.systemManager.stage.focus = null;
        }
    }   

    /**
     *  @inheritDoc
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public function showFocus():void
    {
        if (!showFocusIndicator)
        {
            showFocusIndicator = true;
            if (_lastFocus)
                _lastFocus.drawFocus(true);
        }
    }

    /**
     *  @inheritDoc
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public function hideFocus():void
    {
        // trace("FOcusManger " + this + " Hide Focus");
        if (showFocusIndicator)
        {
            showFocusIndicator = false;
            if (_lastFocus)
                _lastFocus.drawFocus(false);
        }
        // trace("END FOcusManger Hide Focus");
    }
    
    /**
     *  The SystemManager activates and deactivates a FocusManager
     *  if more than one IFocusManagerContainer is visible at the same time.
     *  If the mouse is clicked in an IFocusManagerContainer with a deactivated
     *  FocusManager, the SystemManager will call 
     *  the <code>activate()</code> method on that FocusManager.
     *  The FocusManager that was activated will have its <code>deactivate()</code> method
     *  called prior to the activation of another FocusManager.
     *
     *  <p>The FocusManager adds event handlers that allow it to monitor
     *  focus related keyboard and mouse activity.</p>
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public function activate():void
    {
        // we can get a double activation if we're popping up and becoming visible
        // like the second time a menu appears
        if (activated)
        {
        	// trace("FocusManager is already active " + this);
            return;
        }

        // trace("FocusManager activating = " + this._form.systemManager.loaderInfo.url);
        // trace("FocusManager activating " + this);

        // listen for focus changes, use weak references for the stage
		// form.systemManager can be null if the form is created in a sandbox and 
		// added as a child to the root system manager.
		var sm:ISystemManager = form.systemManager;
		if (sm)
		{
			if (sm.isTopLevelRoot())
			{
		        sm.stage.addEventListener(FocusEvent.MOUSE_FOCUS_CHANGE, mouseFocusChangeHandler, false, 0, true);
		        sm.stage.addEventListener(FocusEvent.KEY_FOCUS_CHANGE, keyFocusChangeHandler, false, 0, true);
	    	    sm.stage.addEventListener(Event.ACTIVATE, activateHandler, false, 0, true);
	        	sm.stage.addEventListener(Event.DEACTIVATE, deactivateHandler, false, 0, true);
	  		}
	  		else
	  		{
		        sm.addEventListener(FocusEvent.MOUSE_FOCUS_CHANGE, mouseFocusChangeHandler, false, 0, true);
		        sm.addEventListener(FocusEvent.KEY_FOCUS_CHANGE, keyFocusChangeHandler, false, 0, true);
	    	    sm.addEventListener(Event.ACTIVATE, activateHandler, false, 0, true);
	        	sm.addEventListener(Event.DEACTIVATE, deactivateHandler, false, 0, true);	  			
	  		}
		}      
	        
        form.addEventListener(FocusEvent.FOCUS_IN, focusInHandler, true);
        form.addEventListener(FocusEvent.FOCUS_OUT, focusOutHandler, true);
        form.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler); 
        form.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownCaptureHandler, true); 
        form.addEventListener(KeyboardEvent.KEY_DOWN, defaultButtonKeyHandler);
        form.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler, true);
        if (sm)
        {
            // AIR Window events, but don't want to link in AIREvent
            // use capture phase because these get sent by the main Window
            // and we might be managing a popup in that window
            sm.addEventListener("windowActivate", activateWindowHandler, true, 0, true);
            sm.addEventListener("windowDeactivate", deactivateWindowHandler, true, 0, true);
        }

        activated = true;
        dispatchEvent(new FlexEvent(FlexEvent.FLEX_WINDOW_ACTIVATE));
        
        // Restore focus to the last control that had it if there was one.
        if (_lastFocus)
            setFocus(_lastFocus);

        if (hasEventListener("activateFM"))
    		dispatchEvent(new Event("activateFM"));

    }

    /**
     *  The SystemManager activates and deactivates a FocusManager
     *  if more than one IFocusManagerContainer is visible at the same time.
     *  If the mouse is clicked in an IFocusManagerContainer with a deactivated
     *  FocusManager, the SystemManager will call 
     *  the <code>activate()</code> method on that FocusManager.
     *  The FocusManager that was activated will have its <code>deactivate()</code> method
     *  called prior to the activation of another FocusManager.
     *
     *  <p>The FocusManager removes event handlers that allow it to monitor
     *  focus related keyboard and mouse activity.</p>
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public function deactivate():void
    {
        // trace("FocusManager deactivating " + this);
        // trace("FocusManager deactivating = " + this._form.systemManager.loaderInfo.url);
         
        // listen for focus changes
		var sm:ISystemManager = form.systemManager;
        if (sm)
        {
			if (sm.isTopLevelRoot())
			{
		        sm.stage.removeEventListener(FocusEvent.MOUSE_FOCUS_CHANGE, mouseFocusChangeHandler);
		        sm.stage.removeEventListener(FocusEvent.KEY_FOCUS_CHANGE, keyFocusChangeHandler);
	    	    sm.stage.removeEventListener(Event.ACTIVATE, activateHandler);
	        	sm.stage.removeEventListener(Event.DEACTIVATE, deactivateHandler);
	  		}
	  		else
	  		{
		        sm.removeEventListener(FocusEvent.MOUSE_FOCUS_CHANGE, mouseFocusChangeHandler);
		        sm.removeEventListener(FocusEvent.KEY_FOCUS_CHANGE, keyFocusChangeHandler);
	    	    sm.removeEventListener(Event.ACTIVATE, activateHandler);
	        	sm.removeEventListener(Event.DEACTIVATE, deactivateHandler);	  			
	  		}
        }

        form.removeEventListener(FocusEvent.FOCUS_IN, focusInHandler, true);
        form.removeEventListener(FocusEvent.FOCUS_OUT, focusOutHandler, true);
        form.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler); 
        form.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDownCaptureHandler, true); 
        form.removeEventListener(KeyboardEvent.KEY_DOWN, defaultButtonKeyHandler);
        // stop listening for default button in Capture phase
        form.removeEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler, true);

        activated = false;
        dispatchEvent(new FlexEvent(FlexEvent.FLEX_WINDOW_DEACTIVATE));

        if (hasEventListener("deactivateFM"))
    		dispatchEvent(new Event("deactivateFM"));
    }

    /**
     *  @inheritDoc
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public function findFocusManagerComponent(
                            o:InteractiveObject):IFocusManagerComponent
    {
    	return findFocusManagerComponent2(o) as IFocusManagerComponent;
    }
    
    
    /**
    * @private
    * 
    * This version of the method differs from the old one to support SWFLoader
    * being in the focusableObjects list but not being a component that
    * gets focus. SWFLoader is in the list of focusable objects so
    * focus may be passed over a bridge to the components on the other
    * side of the bridge.
    */
    private function findFocusManagerComponent2(
                            o:InteractiveObject):DisplayObject

    {
    	try
    	{
	        while (o)
	        {
	            if ((o is IFocusManagerComponent && IFocusManagerComponent(o).focusEnabled) ||
	            	 o is ISWFLoader)
	                return o;
	            
	            o = o.parent;
	        }
	    }
	    catch (error:SecurityError)
	    {
	    	// can happen in a loaded child swf
	    	// trace("findFocusManagerComponent: handling security error");
	    }

        // tab was set somewhere else
        return null;
    }

    /**
     *  @private
     *  Returns true if p is a parent of o.
     */
    private function isParent(p:DisplayObjectContainer, o:DisplayObject):Boolean
    {
        if (p == o)
            return false;
        
        if (p is IRawChildrenContainer)
            return IRawChildrenContainer(p).rawChildren.contains(o);
        
        return p.contains(o);
    }
    
    private function isEnabledAndVisible(o:DisplayObject):Boolean
    {
        var formParent:DisplayObjectContainer = DisplayObjectContainer(form);
        
        while (o != formParent)
        {
            if (o is IUIComponent)
                if (!IUIComponent(o).enabled)
                    return false;
            
            if (o is IVisualElement)
                if (IVisualElement(o).designLayer && !IVisualElement(o).designLayer.effectiveVisibility)
                    return false;
            
            if (!o.visible) 
                return false;
            o = o.parent;

            // if no parent, then not on display list
            if (!o)
                return false;
        }
        return true;
    }

    /**
     *  @private
     */
    private function sortByTabIndex(a:InteractiveObject, b:InteractiveObject):int
    {
        var aa:int = a.tabIndex;
        var bb:int = b.tabIndex;

        if (aa == -1)
            aa = int.MAX_VALUE;
        if (bb == -1)
            bb = int.MAX_VALUE;

        return (aa > bb ? 1 :
                aa < bb ? -1 : sortByDepth(DisplayObject(a), DisplayObject(b)));
    }

    /**
     *  @private
     */
    private function sortFocusableObjectsTabIndex():void
    {
        // trace("FocusableObjectsTabIndex");
        
        focusableCandidates = [];
        
        var n:int = focusableObjects.length;
        for (var i:int = 0; i < n; i++)
        {
            var c:IFocusManagerComponent = focusableObjects[i] as IFocusManagerComponent;
            if ((c && c.tabIndex && !isNaN(Number(c.tabIndex))) ||
                 focusableObjects[i] is ISWFLoader)
            {
                // if we get here, it is a candidate
                focusableCandidates.push(focusableObjects[i]);
            }
        }
        
        focusableCandidates.sort(sortByTabIndex);
    }

    /**
     *  @private
     */
    private function sortByDepth(aa:DisplayObject, bb:DisplayObject):Number
    {
        var val1:String = "";
        var val2:String = "";
        var index:int;
        var tmp:String;
        var tmp2:String;
        var zeros:String = "0000";

        var a:DisplayObject = DisplayObject(aa);
        var b:DisplayObject = DisplayObject(bb);

		// TODO (egreenfi):  If a component lives inside of a group, we care about not its display object index, but
		// its index within the group. See SDK-25144
		
        while (a != DisplayObject(form) && a.parent)
        {
            index = getChildIndex(a.parent, a);
            tmp = index.toString(16);
            if (tmp.length < 4)
            {
                tmp2 = zeros.substring(0, 4 - tmp.length) + tmp;
            }
            val1 = tmp2 + val1;
            a = a.parent;
        }
        
        while (b != DisplayObject(form) && b.parent)
        {
            index = getChildIndex(b.parent, b);
            tmp = index.toString(16);
            if (tmp.length < 4)
            {
                tmp2 = zeros.substring(0, 4 - tmp.length) + tmp;
            }
            val2 = tmp2 + val2;
            b = b.parent;
        }

        return val1 > val2 ? 1 : val1 < val2 ? -1 : 0;
    }

    private function getChildIndex(parent:DisplayObjectContainer, child:DisplayObject):int
    {
        try 
        {
            return parent.getChildIndex(child);
        }
        catch(e:Error)
        {
            if (parent is IRawChildrenContainer)
                return IRawChildrenContainer(parent).rawChildren.getChildIndex(child);
            throw e;
        }
        throw new Error("FocusManager.getChildIndex failed");   // shouldn't ever get here
    }

    /**
     *  @private
     *  Calculate what focusableObjects are valid tab candidates.
     */
    private function sortFocusableObjects():void
    {
        // trace("FocusableObjects " + focusableObjects.length.toString());
        focusableCandidates = [];
        
        var n:int = focusableObjects.length;
        for (var i:int = 0; i < n; i++)
        {
            var c:InteractiveObject = focusableObjects[i];
            // trace("  " + c);
            if (c.tabIndex && !isNaN(Number(c.tabIndex)) && c.tabIndex > 0)
            {
                sortFocusableObjectsTabIndex();
                return;
            }
            focusableCandidates.push(c);
        }
        
        focusableCandidates.sort(sortByDepth);
    }

    /**
     *  Call this method to make the system
     *  think the Enter key was pressed and the defaultButton was clicked
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    mx_internal function sendDefaultButtonEvent():void
    {
        // trace("FocusManager.sendDefaultButtonEvent " + defButton);
        defButton.dispatchEvent(new MouseEvent("click"));
    }

    /**
     *  @private
     *  Do a tree walk and add all children you can find.
     */
    mx_internal function addFocusables(o:DisplayObject, skipTopLevel:Boolean = false):void
    {
        // trace(">>addFocusables " + o);
        if ((o is IFocusManagerComponent) && !skipTopLevel)
        {
			
			var addToFocusables:Boolean = false;
        	if (o is IFocusManagerComponent)
        	{
	            var focusable:IFocusManagerComponent = IFocusManagerComponent(o);
	            if (focusable.focusEnabled)
	            {
	                if (focusable.tabFocusEnabled && isTabVisible(o))
	                {
						addToFocusables = true;	                	
	                }
                }
            }

			if (addToFocusables)
			{            
               	if (focusableObjects.indexOf(o) == -1)
            	{
                    focusableObjects.push(o);
	                calculateCandidates = true;
    	            // trace("FM added " + o);
    	        }
   			}
            o.addEventListener("tabFocusEnabledChange", tabFocusEnabledChangeHandler);
            o.addEventListener("tabIndexChange", tabIndexChangeHandler);

        }
        
        if (o is DisplayObjectContainer)
        {
            var doc:DisplayObjectContainer = DisplayObjectContainer(o);
            // Even if they aren't focusable now,
            // listen in case they become later.
            var checkChildren:Boolean;

            if (o is IFocusManagerComponent)
            {
                o.addEventListener("hasFocusableChildrenChange", hasFocusableChildrenChangeHandler);
                checkChildren = IFocusManagerComponent(o).hasFocusableChildren;
            }
            else
            {
                o.addEventListener("tabChildrenChange", tabChildrenChangeHandler);
                checkChildren = doc.tabChildren;
            }

            if (checkChildren)
            {
                if (o is IRawChildrenContainer)
                {
                    // trace("using view rawChildren");
                    var rawChildren:IChildList = IRawChildrenContainer(o).rawChildren;
                    // recursively visit and add children of components
                    // we don't do this for containers because we get individual
                    // adds for the individual children
                    var i:int;
                    for (i = 0; i < rawChildren.numChildren; i++)
                    {
                        try
                        {
                            addFocusables(rawChildren.getChildAt(i));
                        }
                        catch(error:SecurityError)
                        {
                            // Ignore this child if we can't access it
                            // trace("addFocusables: ignoring security error getting child from rawChildren: " + error);
                        }
                    }

                }
                else
                {
                    // trace("using container's children");
                    // recursively visit and add children of components
                    // we don't do this for containers because we get individual
                    // adds for the individual children
                    for (i = 0; i < doc.numChildren; i++)
                    {
                        try
                        {
                            addFocusables(doc.getChildAt(i));
                        }
                        catch(error:SecurityError)
                        {
                            // Ignore this child if we can't access it
                            // trace("addFocusables: ignoring security error getting child at document." + error);
                        }
                    }
                }
            }
        }
        // trace("<<addFocusables " + o);
    }

    /**
     *  @private
     *  is it really tabbable?
     */
    private function isTabVisible(o:DisplayObject):Boolean
    {
        var s:DisplayObject = DisplayObject(form.systemManager);
        if (!s) return false;

        var p:DisplayObjectContainer = o.parent;
        while (p && p != s)
        {
            if (!p.tabChildren)
                return false;
            if (p is IFocusManagerComponent && !(IFocusManagerComponent(p).hasFocusableChildren))
                return false;
            p = p.parent;
        }
        return true;
    }

    private function isValidFocusCandidate(o:DisplayObject, g:String):Boolean
    {
        if (o is IFocusManagerComponent)
            if (!IFocusManagerComponent(o).focusEnabled)
                return false;

        if (!isEnabledAndVisible(o))
            return false;

        if (o is IFocusManagerGroup)
        {
            // reject if it is in the same tabgroup
            var tg:IFocusManagerGroup = IFocusManagerGroup(o);
            if (g == tg.groupName) return false;
        }
        return true;
    }
    
    private function getIndexOfFocusedObject(o:DisplayObject):int
    {
        if (!o)
            return -1;

        var n:int = focusableCandidates.length;
        // trace(" focusableCandidates " + n);
        var i:int = 0;
        for (i = 0; i < n; i++)
        {
            // trace(" comparing " + focusableCandidates[i]);
            if (focusableCandidates[i] == o)
                return i;
        }

        // no match?  try again with a slower match for certain
        // cases like DG editors
        for (i = 0; i < n; i++)
        {
            var iui:IUIComponent = focusableCandidates[i] as IUIComponent;
            if (iui && iui.owns(o))
                return i;
        }

        return -1;
    }


    private function getIndexOfNextObject(i:int, shiftKey:Boolean, bSearchAll:Boolean, groupName:String):int
    {
        var n:int = focusableCandidates.length;
        var start:int = i;

        while (true)
        {
            if (shiftKey)
                i--;
            else
                i++;
            if (bSearchAll)
            {
                if (shiftKey && i < 0)
                    break;
                if (!shiftKey && i == n)
                    break;
            }
            else
            {
                i = (i + n) % n;
                // came around and found the original
                if (start == i)
                    break;
                // if start is -1, set start to first valid value of i
                if (start == -1)
                    start = i;
            }
            // trace("testing " + focusableCandidates[i]);
            if (isValidFocusCandidate(focusableCandidates[i], groupName))
            {
                // trace(" stopped at " + i);
                var o:DisplayObject = DisplayObject(findFocusManagerComponent2(focusableCandidates[i]));     
                if (o is IFocusManagerGroup)
                {
                    // look around to see if there's an enabled and visible
                    // selected member in the tabgroup, otherwise use the first
                    // one we found.
                    var tg1:IFocusManagerGroup = IFocusManagerGroup(o);
                    for (var j:int = 0; j < focusableCandidates.length; j++)
                    {
                        var obj:DisplayObject = focusableCandidates[j];
                        if (obj is IFocusManagerGroup && isEnabledAndVisible(obj))
                        {
                            var tg2:IFocusManagerGroup = IFocusManagerGroup(obj);
                            if (tg2.groupName == tg1.groupName && tg2.selected)
                            {
                                // if objects of same group have different tab index
                                // skip you aren't selected.
                                if (InteractiveObject(obj).tabIndex != InteractiveObject(o).tabIndex && !tg1.selected)
                                    return getIndexOfNextObject(i, shiftKey, bSearchAll, groupName);

                                i = j;
                                break;
                            }
                        }
                    }

                }
                return i;
            }
        }
        return i;
    }

    /**
     *  @private
     */
    private function setFocusToNextObject(event:FocusEvent):void
    {
     	focusChanged = false;
        if (focusableObjects.length == 0)
            return;

		var focusInfo:FocusInfo = getNextFocusManagerComponent2(event.shiftKey, fauxFocus);
		// trace("winner = ", focusInfo.displayObject);

		// If we are about to wrap focus around, send focus back to the parent.
		if (!popup && (focusInfo.wrapped || !focusInfo.displayObject))
		{
            if (hasEventListener("focusWrapping"))
	    		if (!dispatchEvent(new FocusEvent("focusWrapping", false, true, null, event.shiftKey)))
		    		return;
		}
		
		if (!focusInfo.displayObject)
		{
			event.preventDefault();
			return;
		}

		setFocusToComponent(focusInfo.displayObject, event.shiftKey);		
	}

	private function setFocusToComponent(o:Object, shiftKey:Boolean):void
	{
		focusChanged = false;
		if (o)
		{
            if (hasEventListener("setFocusToComponent"))
    			if (!dispatchEvent(new FocusEvent("setFocusToComponent", false, true, InteractiveObject(o), shiftKey)))
	    			return;

			if (o is IFocusManagerComplexComponent)
			{
				IFocusManagerComplexComponent(o).assignFocus(shiftKey ? "bottom" : "top");
				focusChanged = true;
			}
			else if (o is IFocusManagerComponent)
			{
				setFocus(IFocusManagerComponent(o));
				focusChanged = true;
			}
				
		}
		
	}

	/**
	 *  @private
	 */
	mx_internal function setFocusToNextIndex(index:int, shiftKey:Boolean):void
	{
		if (focusableObjects.length == 0)
			return;
			
        // I think we'll have time to do this here instead of at creation time
        // this makes and orders the focusableCandidates array
        if (calculateCandidates)
        {
            sortFocusableObjects();
            calculateCandidates = false;
        }

		var focusInfo:FocusInfo = getNextFocusManagerComponent2(shiftKey, null, index);			

		// If we are about to wrap focus around, send focus back to the parent.
		if (!popup && focusInfo.wrapped)
		{
            if (hasEventListener("setFocusToNextIndex"))
    			if (!dispatchEvent(new FocusEvent("setFocusToNextIndex", false, true, null, shiftKey)))
	    			return;
		}
		
		setFocusToComponent(focusInfo.displayObject, shiftKey);
	}
	
    /**
     *  @inheritDoc
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public function getNextFocusManagerComponent(
                            backward:Boolean = false):IFocusManagerComponent
	{
		return getNextFocusManagerComponent2(backward, fauxFocus).displayObject as IFocusManagerComponent;
	}
	
	/**
	 * Find the next object to set focus to.
	 * 
	 * @param backward true if moving in the backwards in the tab order, false if moving forward.
	 * @param fromObject object to move focus from, if null move from the current focus.
	 * @param formIndex index to move focus from, if specified use fromIndex to find the 
	 * 		   			object, not fromObject.
	 *  
	 *  @langversion 3.0
	 *  @playerversion Flash 9
	 *  @playerversion AIR 1.1
	 *  @productversion Flex 3
	 */
	private function getNextFocusManagerComponent2(
                            backward:Boolean = false, 
                            fromObject:DisplayObject = null,
                            fromIndex:int = FROM_INDEX_UNSPECIFIED):FocusInfo
                            
    {
        if (focusableObjects.length == 0)
            return null;

        // I think we'll have time to do this here instead of at creation time
        // this makes and orders the focusableCandidates array
        if (calculateCandidates)
        {
            sortFocusableObjects();
            calculateCandidates = false;
        }

        // trace("focus was at " + o);
        // trace("focusableObjects " + focusableObjects.length);
        var i:int = fromIndex;
        if (fromIndex == FROM_INDEX_UNSPECIFIED)
        {
	        // if there is no passed in object, then get the object that has the focus
    	    var o:DisplayObject = fromObject; 
        	if (!o)
        		o = form.systemManager.stage.focus;
        
	        o = DisplayObject(findFocusManagerComponent2(InteractiveObject(o)));
	
	        var g:String = "";
	        if (o is IFocusManagerGroup)
	        {
	            var tg:IFocusManagerGroup = IFocusManagerGroup(o);
	            g = tg.groupName;
	        }
	        i = getIndexOfFocusedObject(o);
        }
        
        // trace(" starting at " + i);
        var bSearchAll:Boolean = false;
        var start:int = i;
        if (i == -1) // we didn't find it
        {
            if (backward)
                i = focusableCandidates.length;
            bSearchAll = true;
            // trace("search all " + i);
        }

        var j:int = getIndexOfNextObject(i, backward, bSearchAll, g);

        // if we wrapped around, get if we have a parent we should pass
        // focus to.
        var wrapped:Boolean = false;
        if (backward)
        {
        	if (j >= i)
        		wrapped = true;
        }
        else if (j <= i)
      		wrapped = true;

		var focusInfo:FocusInfo = new FocusInfo();
		
		focusInfo.displayObject = findFocusManagerComponent2(focusableCandidates[j]);
		focusInfo.wrapped = wrapped;
		
        return focusInfo;
    }


    /**
     *  @private
     */
    private function getTopLevelFocusTarget(o:InteractiveObject):InteractiveObject
    {
        while (o != InteractiveObject(form))
        {
            if (o is IFocusManagerComponent &&
                IFocusManagerComponent(o).focusEnabled &&
                IFocusManagerComponent(o).mouseFocusEnabled &&
                (o is IUIComponent ? IUIComponent(o).enabled : true))
                return o;

            if (hasEventListener("getTopLevelFocusTarget"))
    			if (!dispatchEvent(new FocusEvent("getTopLevelFocusTarget", false, true, o.parent)))
			    	return null;

            o = o.parent;

            if (o == null)
                break;
        }

        return null;
    }

    /**
     *  Returns a String representation of the component hosting the FocusManager object, 
     *  with the String <code>".focusManager"</code> appended to the end of the String.
     *
     *  @return Returns a String representation of the component hosting the FocusManager object, 
     *  with the String <code>".focusManager"</code> appended to the end of the String.
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    override public function toString():String
    {
        return Object(form).toString() + ".focusManager";
    }
    
    /**
     *  @private
     * 
     *  Clear the browser focus component and undo any tab index we may have set.
     */
    private function clearBrowserFocusComponent():void
    {
        if (browserFocusComponent)
        {
            if (browserFocusComponent.tabIndex == LARGE_TAB_INDEX)
                browserFocusComponent.tabIndex = -1;
            
            browserFocusComponent = null;
        }
    }
    
    //--------------------------------------------------------------------------
    //
    //  Event handlers
    //
    //--------------------------------------------------------------------------

    /**
     *  @private
     *  Listen for children being added
     *  and see if they are focus candidates.
     */
    private function addedHandler(event:Event):void
    {
        var target:DisplayObject = DisplayObject(event.target);
        
        // trace("FM: addedHandler: got added for " + target);
        
        // if it is truly parented, add it, otherwise it will get added when the top of the tree
        // gets parented.
        if (target.stage)
        {
            // trace("FM: addedHandler: adding focusables");
            addFocusables(DisplayObject(event.target));
        }
    }

    /**
     *  @private
     *  Listen for children being removed.
     */
    private function removedHandler(event:Event):void
    {
        var i:int;
        var o:DisplayObject = DisplayObject(event.target);
        var focusPaneParent:DisplayObject = focusPane ? focusPane.parent : null;

        // Remove the focusPane to allow the focusOwner to be garbage collected.
        // Avoid recursion by not processing the removal of the focusPane itself.        
        if (focusPaneParent && o != focusPane)
        {
            if (o is DisplayObjectContainer && 
                isParent(DisplayObjectContainer(o), focusPane))
            {
                if (focusPaneParent is ISystemManager)
                    ISystemManager(focusPaneParent).focusPane = null;
                else
                    IUIComponent(focusPaneParent).focusPane = null;
            }
        }
            
        // trace("FM got added for " + event.target);

        if (o is IFocusManagerComponent)
        {
            for (i = 0; i < focusableObjects.length; i++)
            {
                if (o == focusableObjects[i])
                {
                    if (o == _lastFocus)
                    {
                        _lastFocus.drawFocus(false);
                        _lastFocus = null;
                    }
                    // trace("FM removed " + o);
                    focusableObjects.splice(i, 1);
                    focusableCandidates = [];
                    calculateCandidates = true;                 
                    break;
                }
            }
            o.removeEventListener("tabFocusEnabledChange", tabFocusEnabledChangeHandler);
            o.removeEventListener("tabIndexChange", tabIndexChangeHandler);
        }
        removeFocusables(o, false);
    }

    /**
     *  @private
     */
    private function removeFocusables(o:DisplayObject, dontRemoveTabChildrenHandler:Boolean):void
    {
        var i:int;
        if (o is DisplayObjectContainer)
        {
            if (!dontRemoveTabChildrenHandler)
            {
                o.removeEventListener("tabChildrenChange", tabChildrenChangeHandler);
                o.removeEventListener("hasFocusableChildrenChange", hasFocusableChildrenChangeHandler);
            }

            for (i = 0; i < focusableObjects.length; i++)
            {
                if (isParent(DisplayObjectContainer(o), focusableObjects[i]))
                {
                    if (focusableObjects[i] == _lastFocus)
                    {
                        _lastFocus.drawFocus(false);
                        _lastFocus = null;
                    }
                    // trace("FM removed " + focusableObjects[i]);
                    focusableObjects[i].removeEventListener(
                        "tabFocusEnabledChange", tabFocusEnabledChangeHandler);
                    focusableObjects[i].removeEventListener(
                        "tabIndexChange", tabIndexChangeHandler);
                    focusableObjects.splice(i, 1);
                    i = i - 1;  // because increment would skip one
                    
                    focusableCandidates = [];
                    calculateCandidates = true;                 
                }
            }
        }
    }

    /**
     *  @private
     */
    private function showHandler(event:Event):void
    {
		var awm:IActiveWindowManager = 
			IActiveWindowManager(form.systemManager.getImplementation("mx.managers::IActiveWindowManager"));
		if (awm)
        	awm.activate(form); // build a message that does the equal
    }

    /**
     *  @private
     */
    private function hideHandler(event:Event):void
    {
		var awm:IActiveWindowManager = 
			IActiveWindowManager(form.systemManager.getImplementation("mx.managers::IActiveWindowManager"));
		if (awm)
        	awm.deactivate(form); // build a message that does the equal
    }

    /**
     *  @private
     */
    private function childHideHandler(event:Event):void
    {
        var target:DisplayObject = DisplayObject(event.target);
        // trace("FocusManager focusInHandler in  = " + this._form.systemManager.loaderInfo.url);
        // trace("FM " + this + " focusInHandler " + target);

        if (lastFocus && !isEnabledAndVisible(DisplayObject(lastFocus)) && DisplayObject(form).stage)
        {
            DisplayObject(form).stage.focus = null;
            lastFocus = null;
        }
    }
    
    /**
     *  @private
     */
    private function viewHideHandler(event:Event):void
    {
        // Target is the active view that is about to be hidden
        var target:DisplayObjectContainer = event.target as DisplayObjectContainer;
        var lastFocusDO:DisplayObject = lastFocus as DisplayObject;
       
        // If the lastFocus is in the view about to be hidden, clear focus
        if (target && lastFocusDO && target.contains(lastFocusDO))
            lastFocus = null;
    }

    /**
     *  @private
     */
    private function creationCompleteHandler(event:FlexEvent):void
    {
        var o:DisplayObject = DisplayObject(form);
        if (o.parent && o.visible && !activated)
		{
			var awm:IActiveWindowManager = 
				IActiveWindowManager(form.systemManager.getImplementation("mx.managers::IActiveWindowManager"));
			if (awm)
	        	awm.activate(form); // build a message that does the equal
		}
    }

    /**
     *  @private
     *  Add or remove if tabbing properties change.
     */
    private function tabIndexChangeHandler(event:Event):void
    {
        calculateCandidates = true;
    }

    /**
     *  @private
     *  Add or remove if tabbing properties change.
     */
    private function tabFocusEnabledChangeHandler(event:Event):void
    {
        calculateCandidates = true;

        var o:IFocusManagerComponent = IFocusManagerComponent(event.target);
        var n:int = focusableObjects.length;
        for (var i:int = 0; i < n; i++)
        {
            if (focusableObjects[i] == o)
                break;
        }
        if (o.tabFocusEnabled)
        {
            if (i == n && isTabVisible(DisplayObject(o)))
            {
                // trace("FM tpc added " + o);
                // add it if were not already
               	if (focusableObjects.indexOf(o) == -1)
	                focusableObjects.push(o);
            }
        }
        else
        {
            // remove it
            if (i < n)
            {
                // trace("FM tpc removed " + o);
                focusableObjects.splice(i, 1);
            }
        }
    }

    /**
     *  @private
     *  Add or remove if tabbing properties change.
     */
    private function tabChildrenChangeHandler(event:Event):void
    {
        if (event.target != event.currentTarget)
            return;

        calculateCandidates = true;

        var o:DisplayObjectContainer = DisplayObjectContainer(event.target);
        if (o.tabChildren)
        {
            addFocusables(o, true);
        }
        else
        {
            removeFocusables(o, true);
        }
    }

    /**
     *  @private
     *  Add or remove if tabbing properties change.
     */
    private function hasFocusableChildrenChangeHandler(event:Event):void
    {
        if (event.target != event.currentTarget)
            return;

        calculateCandidates = true;

        var o:IFocusManagerComponent = IFocusManagerComponent(event.target);
        if (o.hasFocusableChildren)
        {
            addFocusables(DisplayObject(o), true);
        }
        else
        {
            removeFocusables(DisplayObject(o), true);
        }
    }

    /**
     *  @private
     *  This gets called when mouse clicks on a focusable object.
     *  We block player behavior
     */
    private function mouseFocusChangeHandler(event:FocusEvent):void
    {
        // trace("FocusManager: mouseFocusChangeHandler  in  = " + this._form.systemManager.loaderInfo.url);
    	// trace("FocusManager: mouseFocusChangeHandler " + event);
        
        if (event.isDefaultPrevented())
            return;

        // If relatedObject is null because we don't have access to the 
        // object getting focus then allow the Player to set focus
        // to the object. The isRelatedObjectInaccessible property is 
        // Player 10 only so we have to test if it is available. We
        // will only see isRelatedObjectInaccessible if we are a version "10" swf
        // (-target-player=10). Version "9" swfs will not see the property
        // even if running in Player 10.
        if (event.relatedObject == null && 
            "isRelatedObjectInaccessible" in event &&
            event["isRelatedObjectInaccessible"] == true)
        {
            // lost focus to a control in different sandbox.
            return;
        }
        
        if (event.relatedObject is TextField)
        {
            var tf:TextField = event.relatedObject as TextField;
            if (tf.type == "input" || tf.selectable)
            {
                return; // pass it on
            }
        }

        event.preventDefault();
    }

    /**
     *  @private
     *  This gets called when the tab key is hit.
     */
    mx_internal function keyFocusChangeHandler(event:FocusEvent):void
    {
        // trace("keyFocusChangeHandler handled by " + this);
    	// trace("keyFocusChangeHandler event = " + event);
    	
    	var sm:ISystemManager = form.systemManager;

        if (hasEventListener("keyFocusChange"))
    		if (!dispatchEvent(new FocusEvent("keyFocusChange", false, true, InteractiveObject(event.target))))
	    		return;

        showFocusIndicator = true;
		focusChanged = false;

        var haveBrowserFocusComponent:Boolean = (browserFocusComponent != null);
        if (browserFocusComponent)
            clearBrowserFocusComponent();
        
        // see if we got here from a tab.  We also need to check for 
        // keyCode == 0 because in IE sometimes the first time you tab 
        // in to the flash player, you get keyCode == 0 instead of TAB.
        // Flash Player bug #2295688.
        if ((event.keyCode == Keyboard.TAB || (browserMode && event.keyCode == 0)) 
                && !event.isDefaultPrevented())
        {
            if (haveBrowserFocusComponent)
            {
                if (hasEventListener("browserFocusComponent"))
	    			dispatchEvent(new FocusEvent("browserFocusComponent", false, false, InteractiveObject(event.target)));
				
                return;
            }

            // trace("tabHandled by " + this);
            setFocusToNextObject(event);

            // if we changed focus or if we're the main app
            // eat the event
			if (focusChanged || sm == sm.getTopLevelRoot())
            	event.preventDefault();
        }
    }

    /**
     *  @private
     *  Watch for TAB keys.
     */
    mx_internal function keyDownHandler(event:KeyboardEvent):void
    {
        // trace("onKeyDown handled by " + this);
    	// trace("onKeyDown event = " + event);
		// if the target is in a bridged application, let it handle the click.
		var sm:ISystemManager = form.systemManager;

        if (hasEventListener("keyDownFM"))
    		if (!dispatchEvent(new FocusEvent("keyDownFM", false, true, InteractiveObject(event.target))))
   	    		return;

        if (sm is SystemManager)
            SystemManager(sm).idleCounter = 0;

        if (event.keyCode == Keyboard.TAB)
        {
            lastAction = "KEY";

            // I think we'll have time to do this here instead of at creation time
            // this makes and orders the focusableCandidates array
            if (calculateCandidates)
            {
                sortFocusableObjects();
                calculateCandidates = false;
            }
        }

        if (browserMode)
        {
            if (browserFocusComponent)
                clearBrowserFocusComponent();
            
            if (event.keyCode == Keyboard.TAB && focusableCandidates.length > 0)
            {
                // get the object that has the focus
                var o:DisplayObject = fauxFocus;
				if (!o)
				{
					o = form.systemManager.stage.focus;
				}
				
                // trace("focus was at " + o);
                // trace("focusableObjects " + focusableObjects.length);
                o = DisplayObject(findFocusManagerComponent2(InteractiveObject(o)));
                var g:String = "";
                if (o is IFocusManagerGroup)
                {
                    var tg:IFocusManagerGroup = IFocusManagerGroup(o);
                    g = tg.groupName;
                }

                var i:int = getIndexOfFocusedObject(o);
                var j:int = getIndexOfNextObject(i, event.shiftKey, false, g);
                if (event.shiftKey)
                {
                    if (j >= i)
                    {
                        // we wrapped so let browser have it
                        browserFocusComponent = getBrowserFocusComponent(event.shiftKey);
                        if (browserFocusComponent.tabIndex == -1)
                            browserFocusComponent.tabIndex = 0;
                    }
                }
                else
                {
                    if (j <= i)
                    {
                        // we wrapped so let browser have it
                        browserFocusComponent = getBrowserFocusComponent(event.shiftKey);
                        if (browserFocusComponent.tabIndex == -1)
                            browserFocusComponent.tabIndex = LARGE_TAB_INDEX;
                    }
                }
            }
        }
    }

    /**
     *  @private
     *  Watch for ENTER key.
     */
    private function defaultButtonKeyHandler(event:KeyboardEvent):void
    {        
        var sm:ISystemManager = form.systemManager;
        if (hasEventListener("defaultButtonKeyHandler"))
    		if (!dispatchEvent(new FocusEvent("defaultButtonKeyHandler", false, true)))
	    		return;
            
        if (defaultButtonEnabled && event.keyCode == Keyboard.ENTER &&
            defaultButton && defButton.enabled)
        {
            sendDefaultButtonEvent();
    }
    }

    /**
     *  @private
     *  This gets called when the focus changes due to a mouse click.
     *
     *  Note: If the focus is changing to a TextField, we don't call
     *  setFocus() on it because the player handles it;
     *  calling setFocus() on a TextField which has scrollable text
     *  causes the text to autoscroll to the end, making the
     *  mouse click set the insertion point in the wrong place.
     */
    private function mouseDownCaptureHandler(event:MouseEvent):void
    {
        // trace("FocusManager mouseDownCaptureHandler in  = " + this._form.systemManager.loaderInfo.url);
        // trace("FocusManager mouseDownCaptureHandler target " + event.target);
        showFocusIndicator = false;
    }

    /**
     *  @private
     *  This gets called when the focus changes due to a mouse click.
     *
     *  Note: If the focus is changing to a TextField, we don't call
     *  setFocus() on it because the player handles it;
     *  calling setFocus() on a TextField which has scrollable text
     *  causes the text to autoscroll to the end, making the
     *  mouse click set the insertion point in the wrong place.
     */
    private function mouseDownHandler(event:MouseEvent):void
    {
        // trace("FocusManager mouseDownHandler in  = " + this._form.systemManager.loaderInfo.url);
        // trace("FocusManager mouseDownHandler target " + event.target);
        
		// if the target is in a bridged application, let it handle the click.
		var sm:ISystemManager = form.systemManager;
        var o:DisplayObject = getTopLevelFocusTarget(
            InteractiveObject(event.target));

        if (!o)
            return;

        // trace("FocusManager mouseDownHandler on " + o);
        
        // Make sure the containing component gets notified.
        // As the note above says, we don't set focus to a TextField ever
        // because the player already did and took care of where
        // the insertion point is, and we also don't call setfocus
        // on a component that last the last focused object unless
        // the last action was just to activate the player and didn't
        // involve tabbing or clicking on a component
        if ((o != _lastFocus || lastAction == "ACTIVATE") && !(o is TextField))
            setFocus(IFocusManagerComponent(o));
		else if (_lastFocus)
		{
			// trace("FM: skipped setting focus to " + _lastFocus);
		}
		
        if (hasEventListener("mouseDownFM"))
    		dispatchEvent(new FocusEvent("mouseDownFM", false, false, InteractiveObject(o)));

        lastAction = "MOUSEDOWN";
	
    }
	
	private function getBrowserFocusComponent(shiftKey:Boolean):InteractiveObject
	{
    	var focusComponent:InteractiveObject = form.systemManager.stage.focus;
		
		// if the focus is null it means focus is in an application we
		// don't have access to. Use either the last object or the first
		// object in this focus manager's list.
		if (!focusComponent)
		{
			var index:int = shiftKey ? 0 : focusableCandidates.length - 1;
			focusComponent = focusableCandidates[index];
		}
		
		return focusComponent;
	}	
}

}

import flash.display.DisplayObject;

/** 
 * @private
 * 
 *  Plain old class to return multiple items of info about the potential
 *  change in focus.
 */
class FocusInfo
{
	public var displayObject:DisplayObject;	// object to get focus
	public var wrapped:Boolean;				// true if focus wrapped around
}
