////////////////////////////////////////////////////////////////////////////////
//
//  ADOBE SYSTEMS INCORPORATED
//  Copyright 2006-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.events.ContextMenuEvent;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IEventDispatcher;
import flash.events.IOErrorEvent;
import flash.events.MouseEvent;
import flash.events.ProgressEvent;
import flash.geom.Point;
import flash.system.ApplicationDomain;
import flash.text.TextField;
import flash.text.TextFieldType;
import flash.ui.Mouse;
import mx.core.EventPriority;
import mx.core.FlexGlobals;
import mx.core.FlexSprite;
import mx.core.mx_internal;
import mx.core.ISystemCursorClient;
import mx.core.IUIComponent;
import mx.events.Request;
import mx.styles.CSSStyleDeclaration;
import mx.styles.StyleManager;
import mx.core.IFlexModuleFactory;

use namespace mx_internal;

[ExcludeClass]

/**
 *  @private
 */
public class CursorManagerImpl extends EventDispatcher implements ICursorManager
{
    include "../core/Version.as";

    //--------------------------------------------------------------------------
    //
    //  Class variables
    //
    //--------------------------------------------------------------------------

    /**
     *  @private
     */
    private static var instance:ICursorManager;

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

    //--------------------------------------------------------------------------
    //
    //  Class methods
    //
    //--------------------------------------------------------------------------

    /**
     *  @private
     */
    public static function getInstance():ICursorManager
    {
        if (!instance)
            instance = new CursorManagerImpl();

        return instance;
    }

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

    /**
     *  @private
     */
    public function CursorManagerImpl(systemManager:ISystemManager = null)
    {
        super();

        if (instance && !systemManager)
            throw new Error("Instance already exists.");

		if (systemManager)
			this.systemManager = systemManager as ISystemManager;
		else
			this.systemManager = SystemManagerGlobals.topLevelSystemManagers[0] as ISystemManager;

		if (mixins)
		{
			var n:int = mixins.length;
			for (var i:int = 0; i < n; i++)
			{
				new mixins[i](this);
		
			}
		}
    }

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

    /**
     *  @private
     */
    private var nextCursorID:int = 1;
    
    /**
     *  @private
     */
    private var cursorList:Array = [];
    
    /**
     *  @private
     */
    private var busyCursorList:Array = [];
    
    /**
     *  @private
     */
    mx_internal var initialized:Boolean = false;
    
    /**
     *  @private
     */
    mx_internal var cursorHolder:Sprite;
    
    /**
     *  @private
     */
    private var currentCursor:DisplayObject;

	/**
     *  @private
     */
	private var listenForContextMenu:Boolean = false;
    
    /*******************************************************************
     * Regarding overTextField, showSystemCursor, and showCustomCursor:
     *    Don't modify or read these variables unless you are certain
     *    you will not create race conditions. E.g. you may get the
     *    wrong (or no) cursor, and get stuck in an inconsistent state.
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
     
    /**
     *  @private
     */
    private var overTextField:Boolean = false;
     
    /**
     *  @private
     */
    private var overLink:Boolean = false;
     
    /**
     *  @private
     */
    private var showSystemCursor:Boolean = false;
    
    /**
     *  @private
     */
    private var showCustomCursor:Boolean = false;
    
    /**
     *  @private
     * 
     * State variable -- set when there is a custom cursor and the
     * mouse has left the stage. Upon return, mouseMoveHandler will
     * restore the custom cursor and remove the system cursor.
     */
    private var customCursorLeftStage:Boolean = false;
    
    /*******************************************************************/

    /**
     *  @private
     */
    mx_internal var systemManager:ISystemManager = null;
    
    /**
     *  @private
     */
    mx_internal var sandboxRoot:IEventDispatcher = null;
    
    /**
     *  @private
     */
    private var sourceArray:Array = [];

    //--------------------------------------------------------------------------
    //
    //  Properties
    //
    //--------------------------------------------------------------------------

    //----------------------------------
    //  currentCursorID
    //----------------------------------

    /**
     *  @private
     */
    mx_internal var _currentCursorID:int = 0 /* CursorManager.NO_CURSOR */;

    /**
     *  ID of the current custom cursor,
     *  or CursorManager.NO_CURSOR if the system cursor is showing.
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public function get currentCursorID():int
    {
        return _currentCursorID;
    }
    
    /**
     *  @private
     */
    public function set currentCursorID(value:int):void
    {
        _currentCursorID = value;

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

    //----------------------------------
    //  currentCursorXOffset
    //----------------------------------

    /**
     *  @private
     */
    mx_internal var _currentCursorXOffset:Number = 0;

    /**
     *  The x offset of the custom cursor, in pixels,
     *  relative to the mouse pointer.
     *       
     *  @default 0
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public function get currentCursorXOffset():Number 
    {
        return _currentCursorXOffset;
    }
    
    /**
     *  @private
     */
    public function set currentCursorXOffset(value:Number):void
    {
        _currentCursorXOffset = value;

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

    //----------------------------------
    //  currentCursorYOffset
    //----------------------------------

    /**
     *  @private
     */
    mx_internal var _currentCursorYOffset:Number = 0;

    /**
     *  The y offset of the custom cursor, in pixels,
     *  relative to the mouse pointer.
     *
     *  @default 0
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public function get currentCursorYOffset():Number 
    {
        return _currentCursorYOffset;
    }
    
    /**
     *  @private
     */
    public function set currentCursorYOffset(value:Number):void
    {
        _currentCursorYOffset = value;

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

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

    /**
     *  Makes the cursor visible.
     *  Cursor visibility is not reference-counted.
     *  A single call to the <code>showCursor()</code> method
     *  always shows the cursor regardless of how many calls
     *  to the <code>hideCursor()</code> method were made.
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public function showCursor():void
    {
        if (cursorHolder)
	        cursorHolder.visible = true;

        if (hasEventListener("showCursor"))
    		dispatchEvent(new Event("showCursor"));
    }
    
    /**
     *  Makes the cursor invisible.
     *  Cursor visibility is not reference-counted.
     *  A single call to the <code>hideCursor()</code> method
     *  always hides the cursor regardless of how many calls
     *  to the <code>showCursor()</code> method were made.
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public function hideCursor():void
    {
    	if (cursorHolder)
	        cursorHolder.visible = false;
		
        if (hasEventListener("hideCursor"))
    		dispatchEvent(new Event("hideCursor"));

    }

    /**
     *  Creates a new cursor and sets an optional priority for the cursor.
     *  Adds the new cursor to the cursor list.
     *
     *  @param cursorClass Class of the cursor to display.
     *
     *  @param priority Integer that specifies
     *  the priority level of the cursor.
     *  Possible values are <code>CursorManagerPriority.HIGH</code>,
     *  <code>CursorManagerPriority.MEDIUM</code>, and <code>CursorManagerPriority.LOW</code>.
     *
     *  @param xOffset Number that specifies the x offset
     *  of the cursor, in pixels, relative to the mouse pointer.
     *
     *  @param yOffset Number that specifies the y offset
     *  of the cursor, in pixels, relative to the mouse pointer.
     *
     *  @param setter The IUIComponent that set the cursor. Necessary (in multi-window environments) 
     *  to know which window needs to display the cursor. 
     * 
     *  @return The ID of the cursor.
     *
     *  @see mx.managers.CursorManagerPriority
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public function setCursor(cursorClass:Class, priority:int = 2,
                                     xOffset:Number = 0,
                                     yOffset:Number = 0):int 
    {
        if (hasEventListener("setCursor"))
        {
		    var event:Request = new Request("setCursor", false, true);
		    event.value = [ cursorClass, priority, xOffset, yOffset ];
		    if (!dispatchEvent(event))
		    {
			    return event.value as int;
		    }
        }

        var cursorID:int = nextCursorID++;
        
        // Create a new CursorQueueItem.
        var item:CursorQueueItem = new CursorQueueItem();
        item.cursorID = cursorID;
        item.cursorClass = cursorClass;
        item.priority = priority;
        item.x = xOffset;
        item.y = yOffset;
        if (systemManager)
        	item.systemManager = systemManager;
        else
        	item.systemManager = FlexGlobals.topLevelApplication.systemManager;
        
        // Push it onto the cursor list.
        cursorList.push(item);
        
        // Re-sort the cursor list based on priority level.
        cursorList.sort(priorityCompare);

        // Determine which cursor to display
        showCurrentCursor();
        
        return cursorID;
    }
    
    /**
     *  @private
     */
    private function priorityCompare(a:CursorQueueItem, b:CursorQueueItem):int
    {
        if (a.priority < b.priority)
            return -1;
        else if (a.priority == b.priority)
            return 0;
        
        return 1;
    }

    /**
     *  Removes a cursor from the cursor list.
     *  If the cursor being removed is the currently displayed cursor,
     *  the CursorManager displays the next cursor in the list, if one exists.
     *  If the list becomes empty, the CursorManager displays
     *  the default system cursor.
     *
     *  @param cursorID ID of cursor to remove.
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public function removeCursor(cursorID:int):void 
    {
        if (hasEventListener("removeCursor"))
    		if (!dispatchEvent(new Request("removeCursor", false, true, cursorID)))
	    		return;

        for (var i:Object in cursorList)
        {
            var item:CursorQueueItem = cursorList[i];
            if (item.cursorID == cursorID)
            {
                // Remove the element from the array.
                cursorList.splice(i, 1); 

                // Determine which cursor to display.
                showCurrentCursor();
                    
                break;
            }
        }
    }
    
    /**
     *  Removes all of the cursors from the cursor list
     *  and restores the system cursor.
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public function removeAllCursors():void
    {
        if (hasEventListener("removeAllCursors"))
    		if (!dispatchEvent(new Event("removeAllCursors", false, true)))
	    		return;

        cursorList.splice(0);
        
        showCurrentCursor();
    }

    /**
     *  Displays the busy cursor.
     *  The busy cursor has a priority of CursorManagerPriority.LOW.
     *  Therefore, if the cursor list contains a cursor
     *  with a higher priority, the busy cursor is not displayed 
     *  until you remove the higher priority cursor.
     *  To create a busy cursor at a higher priority level,
     *  use the <code>setCursor()</code> method.
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public function setBusyCursor():void 
    {
        if (hasEventListener("setBusyCursor"))
    		if (!dispatchEvent(new Event("setBusyCursor", false, true)))
	    		return;

        var cursorManagerStyleDeclaration:CSSStyleDeclaration =
            StyleManager.getStyleManager(systemManager as IFlexModuleFactory).
            getMergedStyleDeclaration("mx.managers.CursorManager");
        
        var busyCursorClass:Class =
            cursorManagerStyleDeclaration.getStyle("busyCursor");
        
        busyCursorList.push(setCursor(busyCursorClass, CursorManagerPriority.LOW));
    }

    /**
     *  Removes the busy cursor from the cursor list.
     *  If other busy cursor requests are still active in the cursor list,
     *  which means you called the <code>setBusyCursor()</code> method more than once,
     *  a busy cursor does not disappear until you remove
     *  all busy cursors from the list.
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public function removeBusyCursor():void 
    {
        if (hasEventListener("removeBusyCursor"))
    		if (!dispatchEvent(new Event("removeBusyCursor", false, true)))
	    		return;

        if (busyCursorList.length > 0)
            removeCursor(int(busyCursorList.pop()));
    }

    /**
     *  @private
     *  Decides what cursor to display.
     */
    private function showCurrentCursor():void 
    {
        // if there are custom cursors...
        if (cursorList.length > 0)
        {
            if (!initialized)
            {
				initialized = true;

                var e:Event;
                if (hasEventListener("initialize"))
                {
                    e = new Event("initialize", false, true);
                }
				if (!e || dispatchEvent(e))
				{
					// The first time a cursor is requested of the CursorManager,
					// create a Sprite to hold the cursor symbol
					cursorHolder = new FlexSprite();
					cursorHolder.name = "cursorHolder";
					cursorHolder.mouseEnabled = false;
					cursorHolder.mouseChildren = false;
               		systemManager.cursorChildren.addChild(cursorHolder);
				}
            }

            // Get the top most cursor.
            var item:CursorQueueItem = cursorList[0];
                
            // If the system cursor was being displayed, hide it.
            if (currentCursorID == CursorManager.NO_CURSOR)
                Mouse.hide();
			
            // If the current cursor has changed...
            if (item.cursorID != currentCursorID)
            {
                if (cursorHolder.numChildren > 0)
                    cursorHolder.removeChildAt(0);
                
                currentCursor = new item.cursorClass(); 
                
                if (currentCursor)
                {
                    if (currentCursor is InteractiveObject)
                        InteractiveObject(currentCursor).mouseEnabled = false;
                    if (currentCursor is DisplayObjectContainer)
                        DisplayObjectContainer(currentCursor).mouseChildren = false;
                    cursorHolder.addChild(currentCursor);
                    
                    addContextMenuHandlers();

					var pt:Point;
                    // make sure systemManager is not other implementation of ISystemManager
                    if (systemManager is SystemManager)
                    {
						pt = new Point(SystemManager(systemManager).mouseX + item.x, SystemManager(systemManager).mouseY + item.y);
						pt = SystemManager(systemManager).localToGlobal(pt);
						pt = cursorHolder.parent.globalToLocal(pt);
                    	cursorHolder.x = pt.x;
                    	cursorHolder.y = pt.y;
                    }
                    // WindowedSystemManager
                    else if (systemManager is DisplayObject)
                    {
						pt = new Point(DisplayObject(systemManager).mouseX + item.x, DisplayObject(systemManager).mouseY + item.y);
						pt = DisplayObject(systemManager).localToGlobal(pt);
						pt = cursorHolder.parent.globalToLocal(pt);
                    	cursorHolder.x = DisplayObject(systemManager).mouseX + item.x;
                    	cursorHolder.y = DisplayObject(systemManager).mouseY + item.y;
                    }
                    // otherwise
                    else
                    {
                    	cursorHolder.x = item.x;
                    	cursorHolder.y = item.y;
                    }

                    var e2:Event;
                    if (hasEventListener("addMouseMoveListener"))
                        e2 = new Event("addMouseMoveListener", false, true);
					if (!e2 || dispatchEvent(e2))
						systemManager.stage.addEventListener(MouseEvent.MOUSE_MOVE,
                                                   mouseMoveHandler,true,EventPriority.CURSOR_MANAGEMENT);
                    
                    var e3:Event;
                    if (hasEventListener("addMouseOutListener"))
                        e3 = new Event("addMouseOutListener", false, true);
					if (!e3 || dispatchEvent(e3))
						systemManager.stage.addEventListener(MouseEvent.MOUSE_OUT,
                                                   mouseOutHandler,true,EventPriority.CURSOR_MANAGEMENT);
                    
                }
            	
                currentCursorID = item.cursorID;
                currentCursorXOffset = item.x;
                currentCursorYOffset = item.y;
            }
        }
        else
        {
            showCustomCursor = false;

            if (currentCursorID != CursorManager.NO_CURSOR)
            {
                // There is no cursor in the cursor list to display,
                // so cleanup and restore the system cursor.
                currentCursorID = CursorManager.NO_CURSOR;
                currentCursorXOffset = 0;
                currentCursorYOffset = 0;

                var e4:Event;
                if (hasEventListener("removeMouseMoveListener"))
                    e4 = new Event("removeMouseMoveListener", false, true)
				if (!e4 || dispatchEvent(e4))
                {
               		systemManager.stage.removeEventListener(MouseEvent.MOUSE_MOVE,
                                          mouseMoveHandler,true);
                }

                var e5:Event;
                if (hasEventListener("removeMouseMoveListener"))
                    e5 = new Event("removeMouseOutListener", false, true)
				if (!e5 || dispatchEvent(e5))
                {
               		systemManager.stage.removeEventListener(MouseEvent.MOUSE_OUT,
                                          mouseOutHandler,true);
                }

                cursorHolder.removeChild(currentCursor);
                
				removeContextMenuHandlers();
		    }
            Mouse.show();
        }
    }
    
    /**
     *  @private
     */
    private function addContextMenuHandlers():void
    {
        if (!listenForContextMenu)
        {
            const app:InteractiveObject = systemManager.document as InteractiveObject;
        	const sm:InteractiveObject = systemManager as InteractiveObject;
        	
        	if (app && app.contextMenu)
        	{
        		app.contextMenu.addEventListener(ContextMenuEvent.MENU_SELECT, contextMenu_menuSelectHandler,
        		                                 true, EventPriority.CURSOR_MANAGEMENT);
        		listenForContextMenu = true;
        	}
        	
        	if (sm && sm.contextMenu)
        	{
        		sm.contextMenu.addEventListener(ContextMenuEvent.MENU_SELECT, contextMenu_menuSelectHandler,
        		                                true, EventPriority.CURSOR_MANAGEMENT);
        		listenForContextMenu = true;
        	}     	
        }
    }
    
    /**
     *  @private
     */
    private function removeContextMenuHandlers():void
    {
        if (listenForContextMenu)
        {
            const app:InteractiveObject = systemManager.document as InteractiveObject;
        	const sm:InteractiveObject = systemManager as InteractiveObject;
        	
        	if (app && app.contextMenu)
        		app.contextMenu.removeEventListener(ContextMenuEvent.MENU_SELECT, contextMenu_menuSelectHandler, true);

        	if (sm && sm.contextMenu)
        		sm.contextMenu.removeEventListener(ContextMenuEvent.MENU_SELECT, contextMenu_menuSelectHandler, true);
   
        	listenForContextMenu = false; 	
        }
    }
    
    /**
     *  @private
     *  Called by other components if they want to display
     *  the busy cursor during progress events.
     */
    public function registerToUseBusyCursor(source:Object):void
    {
        if (hasEventListener("registerToUseBusyCursor"))
    		if (!dispatchEvent(new Request("registerToUseBusyCursor", false, true, source)))
	    		return;

        if (source && source is EventDispatcher) 
        {
            source.addEventListener(ProgressEvent.PROGRESS, progressHandler);
            source.addEventListener(Event.COMPLETE, completeHandler);
            source.addEventListener(IOErrorEvent.IO_ERROR, completeHandler);
        }
    }

    /**
     *  @private
     *  Called by other components to unregister
     *  a busy cursor from the progress events.
     */
    public function unRegisterToUseBusyCursor(source:Object):void
    {
        if (hasEventListener("unRegisterToUseBusyCursor"))
    		if (!dispatchEvent(new Request("unRegisterToUseBusyCursor", false, true, source)))
	    		return;

        if (source && source is EventDispatcher) 
        {
            source.removeEventListener(ProgressEvent.PROGRESS, progressHandler);
            source.removeEventListener(Event.COMPLETE, completeHandler);
            source.removeEventListener(IOErrorEvent.IO_ERROR, completeHandler);
        }
    }
    
    /**
     *  @private
     *  Called when contextMenu is opened
     */
    private function contextMenu_menuSelectHandler(event:ContextMenuEvent):void
    {
    	showCustomCursor = true; // Restore the custom cursor
    	// Standalone player doesn't initially send mouseMove when the contextMenu is closed,
    	// so we need to listen for mouseOver as well.   	
    	sandboxRoot.addEventListener(MouseEvent.MOUSE_OVER, mouseOverHandler);
    }
    
    /**
     *  @private
     */
    private function mouseOverHandler(event:MouseEvent):void
    {
    	sandboxRoot.removeEventListener(MouseEvent.MOUSE_OVER, mouseOverHandler);
    	mouseMoveHandler(event);
    }
    
    /**
     *  @private
     */
    private function findSource(target:Object):int
    {
        var n:int = sourceArray.length;
        for (var i:int = 0; i < n; i++)
        {
            if (sourceArray[i] === target)
                return i;
        }
        return -1;
    }

    //--------------------------------------------------------------------------
    //
    //  Event handlers
    //
    //--------------------------------------------------------------------------

    /**
     *  @private
     * 
     * Handles the mouse leaving the stage; hides the custom cursor and restores the system cursor.
     */
    mx_internal function mouseOutHandler(event:MouseEvent):void
    {
        // relatedObject==null implies the mouse left the stage.
        // this also fires when you are returning from a context menu click.
        //
        // it sometimes fires after you drag off the stage, and back to the stage quickly,
        // and let go of the button -- this seems like a player bug
        if ((event.relatedObject == null) && (cursorList.length > 0))
        {
            //trace("mouseOutHandler", event);
            
            // this will get unset in mouseMoveHandler (since that fires when
            // the mouse returns/glides over the stage)
            customCursorLeftStage = true;
            hideCursor();
            Mouse.show();
        }
    }

    /**
     *  @private
     */
    mx_internal function mouseMoveHandler(event:MouseEvent):void
    {
        
		var pt:Point = new Point(event.stageX, event.stageY);
		pt = cursorHolder.parent.globalToLocal(pt);
		pt.x += currentCursorXOffset;
		pt.y += currentCursorYOffset;
       	cursorHolder.x = pt.x;
       	cursorHolder.y = pt.y;

        var target:Object = event.target;
        
        var isInputTextField:Boolean = 
            (target is TextField && target.type == TextFieldType.INPUT) ||
                (target is ISystemCursorClient && 
                    ISystemCursorClient(target).showSystemCursor);
        
        // Do target test.
        if (!overTextField && isInputTextField)
        {   
            overTextField = true;
            showSystemCursor = true;
        } 
        else if (overTextField && !isInputTextField)
        {
            overTextField = false;
            showCustomCursor = true;
        }
		else
		{
			showCustomCursor = true
		}
    
        // Handle switching between system and custom cursor.
        if (showSystemCursor)
        {
            showSystemCursor = false;
			cursorHolder.visible = false;
            Mouse.show();
        }
        if (showCustomCursor)
        {
            showCustomCursor = false;
			cursorHolder.visible = true;
            Mouse.hide();

            if (hasEventListener("showCustomCursor"))
	    		dispatchEvent(new Event("showCustomCursor"));
        }
    }
    
    /**
     *  @private
     *  Displays the busy cursor if a component is in a busy state.
     */
    private function progressHandler(event:ProgressEvent):void
    {
        // Only pay attention to the first progress call. Ignore all others.
        var sourceIndex:int = findSource(event.target);
        if (sourceIndex == -1)
        {
            // Add the target to the list of objects we are listening for.
            sourceArray.push(event.target);
            
            setBusyCursor();
        }
    }
    
    /**
     *  @private
     */
    private function completeHandler(event:Event):void
    {
        var sourceIndex:int = findSource(event.target);
        if (sourceIndex != -1)
        {
            // Remove from the list of targets we are listening to.
            sourceArray.splice(sourceIndex, 1);
            
            removeBusyCursor();
        }
    }

}

}

import mx.managers.CursorManager;
import mx.managers.CursorManagerPriority;
import mx.managers.ISystemManager;

////////////////////////////////////////////////////////////////////////////////
//
//  Helper class: CursorQueueItem
//
////////////////////////////////////////////////////////////////////////////////

/**
 *  @private
 */
class CursorQueueItem
{
    include "../core/Version.as";

    //--------------------------------------------------------------------------
    //
    //  Constructor
    //
    //--------------------------------------------------------------------------
    
    /**
     *  Constructor.
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public function CursorQueueItem()
    {
        super();
    }

    //--------------------------------------------------------------------------
    //
    //  Properties
    //
    //--------------------------------------------------------------------------

    /**
     *  @private
     */
    public var cursorID:int = CursorManager.NO_CURSOR;

    /**
     *  @private
     */
    public var cursorClass:Class = null;

    /**
     *  @private
     */
    public var priority:int = CursorManagerPriority.MEDIUM;
    
     /**
     *  @private
     */
    public var systemManager:ISystemManager;

    /**
     *  @private
     */
    public var x:Number;

    /**
     *  @private
     */
    public var y:Number;
}

