//////////////////////////////////////////////////////////////////////////////// // // Licensed to the Apache Software Foundation (ASF) under one or more // contributor license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright ownership. // The ASF licenses this file to You under the Apache License, Version 2.0 // (the "License"); you may not use this file except in compliance with // the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// package mx.managers { import flash.accessibility.Accessibility; import flash.accessibility.AccessibilityProperties; import flash.display.DisplayObject; import flash.display.DisplayObjectContainer; import flash.display.Graphics; import flash.display.InteractiveObject; import flash.display.Sprite; import flash.display.Stage; import flash.events.Event; import flash.events.EventDispatcher; import flash.events.MouseEvent; import flash.geom.Point; import flash.geom.Rectangle; import flash.system.Capabilities; import mx.automation.IAutomationObject; import mx.core.FlexGlobals; import mx.core.FlexSprite; import mx.core.FlexVersion; import mx.core.IChildList; import mx.core.IFlexDisplayObject; import mx.core.IFlexModule; import mx.core.IFlexModuleFactory; import mx.core.IInvalidating; import mx.core.ILayoutDirectionElement; import mx.core.IUIComponent; import mx.core.LayoutDirection; import mx.core.UIComponent; import mx.core.UIComponentGlobals; import mx.core.mx_internal; import mx.effects.Blur; import mx.effects.Fade; import mx.effects.IEffect; import mx.events.DynamicEvent; import mx.events.EffectEvent; import mx.events.FlexEvent; import mx.events.FlexMouseEvent; import mx.events.Request; import mx.managers.systemClasses.ActiveWindowManager; import mx.styles.IStyleClient; use namespace mx_internal; [ExcludeClass] /** * @private * The PopUpManager singleton class creates new top-level windows and * places or removes those windows from the layer on top of all other * visible windows. See the SystemManager for a description of the layering. * It is used for popup dialogs, menus, and dropdowns in the ComboBox control * and in similar components. * *

The PopUpManager also provides modality, so that windows below the popup * cannot receive mouse events, and also provides an event if the user clicks * the mouse outside the window so the developer can choose to dismiss * the window or warn the user.

* * @see PopUpManagerChildList */ public class PopUpManagerImpl extends EventDispatcher implements IPopUpManager { include "../core/Version.as"; //-------------------------------------------------------------------------- // // Class variables // //-------------------------------------------------------------------------- /** * @private */ private static var instance:IPopUpManager; /** * @private * * Place to hook in additional classes */ public static var mixins:Array; mx_internal static var popUpInfoClass:Class; mx_internal static function createPopUpData():PopUpData { if (!popUpInfoClass) return new PopUpData(); return new popUpInfoClass() as PopUpData; } //-------------------------------------------------------------------------- // // Class methods // //-------------------------------------------------------------------------- /** * @private */ private static function weakDependency():void { ActiveWindowManager }; /** * @private */ public static function getInstance():IPopUpManager { if (!instance) instance = new PopUpManagerImpl(); return instance; } //-------------------------------------------------------------------------- // // Constructor // //-------------------------------------------------------------------------- /** * @private */ public function PopUpManagerImpl() { super(); if (mixins) { var n:int = mixins.length; for (var i:int = 0; i < n; i++) { new mixins[i](this); } } if (hasEventListener("initialize")) dispatchEvent(new Event("initialize")); } //-------------------------------------------------------------------------- // // Variables // //-------------------------------------------------------------------------- /** * @private * The class used to create the shield that makes a window appear modal. */ mx_internal var modalWindowClass:Class; /** * @private * An array of information about currently active popups */ mx_internal var popupInfo:Array = []; //-------------------------------------------------------------------------- // // Methods // //-------------------------------------------------------------------------- /** * Creates a top-level window and places it above other windows in the * z-order. * It is good practice to call the removePopUp() method * to remove popups created by using the createPopUp() method. * * If the class implements IFocusManagerContainer, the window will have its * own FocusManager so that, if the user uses the TAB key to navigate between * controls, only the controls in the window will be accessed. * *

Example

* *
pop = mx.managers.PopUpManager.createPopUp(pnl, TitleWindow, false); 
* *

Creates a popup window based on the TitleWindow class, using pnl as the MovieClip * for determining where to place the popup. It is defined to be a non-modal window * meaning that other windows can receive mouse events

* * @param parent DisplayObject to be used for determining which SystemManager's layers * to use and optionally the reference point for centering the new * top level window. It may not be the actual parent of the popup as all popups * are parented by the SystemManager. * * @param className Class of object that is to be created for the popup. * The class must implement IFlexDisplayObject. * * @param modal If true, the window is modal which means that * the user will not be able to interact with other popups until the window * is removed. * * @param childList The child list in which to add the popup. * One of PopUpManagerChildList.APPLICATION, * PopUpManagerChildList.POPUP, * or PopUpManagerChildList.PARENT (default). * * @param moduleFactory The moduleFactory where this pop-up should look for * its embedded fonts and style manager. * * @return Reference to new top-level window. * * @see PopUpManagerChildList * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function createPopUp(parent:DisplayObject, className:Class, modal:Boolean = false, childList:String = null, moduleFactory:IFlexModuleFactory = null):IFlexDisplayObject { const window:IUIComponent = new className(); addPopUp(window, parent, modal, childList, moduleFactory); return window; } /** * Pops up a top-level window. * It is good practice to call removePopUp() to remove popups * created by using the createPopUp() method. * If the class implements IFocusManagerContainer, the window will have its * own FocusManager so that, if the user uses the TAB key to navigate between * controls, only the controls in the window will be accessed. * *

Example

* *
var tw = new TitleWindow();
     *    tw.title = "My Title";
     *    mx.managers.PopUpManager.addPopUp(tw, pnl, false);
* *

Creates a popup window using the tw instance of the * TitleWindow class and pnl as the Sprite for determining * where to place the popup. * It is defined to be a non-modal window.

* * @param window The IFlexDisplayObject to be popped up. * * @param parent DisplayObject to be used for determining which SystemManager's layers * to use and optionally the reference point for centering the new * top level window. It may not be the actual parent of the popup as all popups * are parented by the SystemManager. Also, it must not be a descendant of * the popup. * * @param modal If true, the window is modal which means that * the user will not be able to interact with other popups until the window * is removed. * * @param childList The child list in which to add the pop-up. * One of PopUpManagerChildList.APPLICATION, * PopUpManagerChildList.POPUP, * or PopUpManagerChildList.PARENT (default). * * @param moduleFactory The moduleFactory where this pop-up should look for * its embedded fonts and style manager. * * @see PopUpManagerChildList * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function addPopUp(window:IFlexDisplayObject, parent:DisplayObject, modal:Boolean = false, childList:String = null, moduleFactory:IFlexModuleFactory = null):void { // trace("POPUP: window is " + window); // All popups go on the local root. // trace("POPUP: root is " + parent.root); // trace("POPUP: initial parent is " + parent); const visibleFlag:Boolean = window.visible; if (parent is IUIComponent && window is IUIComponent && IUIComponent(window).document == null) IUIComponent(window).document = IUIComponent(parent).document; if (window is IFlexModule && IFlexModule(window).moduleFactory == null) { if (moduleFactory) IFlexModule(window).moduleFactory = moduleFactory; else if (parent is IUIComponent && IUIComponent(parent).document is IFlexModule) IFlexModule(window).moduleFactory = IFlexModule(IUIComponent(parent).document).moduleFactory; } var sm:ISystemManager = getTopLevelSystemManager(parent); var children:IChildList; var topMost:Boolean; if (!sm) { // check if parent is our sandbox root sm = ISystemManager(SystemManagerGlobals.topLevelSystemManagers[0]); if (sm.getSandboxRoot() != parent) { //trace("error: popup root was not SystemManager"); return; // and maybe a nice error message } } // smp is the actual systemManager that will parent the popup // it might get changed by the request var smp:ISystemManager = sm; if (hasEventListener("addPopUp")) { var request:Request = new Request("addPopUp", false, true, { parent: parent, sm: sm, modal: modal, childList: childList} ); if (!dispatchEvent(request)) smp = request.value as ISystemManager; } if (window is IUIComponent) IUIComponent(window).isPopUp = true; if (!childList || childList == PopUpManagerChildList.PARENT) topMost = smp.popUpChildren.contains(parent); else topMost = (childList == PopUpManagerChildList.POPUP); children = topMost ? smp.popUpChildren : smp; if (DisplayObject(window).parent != children) children.addChild(DisplayObject(window)); window.visible = false; const o:PopUpData = createPopUpData(); o.owner = DisplayObject(window); o.topMost = topMost; o.systemManager = smp; popupInfo.push(o); var awm:IActiveWindowManager = IActiveWindowManager(smp.getImplementation("mx.managers::IActiveWindowManager")); if (window is IFocusManagerContainer) { if (IFocusManagerContainer(window).focusManager) { awm.addFocusManager(IFocusManagerContainer(window)); } else // Popups get their own focus loop IFocusManagerContainer(window).focusManager = new FocusManager(IFocusManagerContainer(window), true); } if (hasEventListener("addPlaceHolder")) { var event:DynamicEvent = new DynamicEvent("addPlaceHolder"); event.sm = sm; event.window = window; dispatchEvent(event); } // force into automation hierarchy if (window is IAutomationObject) IAutomationObject(window).showInAutomationHierarchy = true; if (window is ILayoutManagerClient ) UIComponentGlobals.layoutManager.validateClient(ILayoutManagerClient (window), true); o.parent = parent; if (window is IUIComponent) { IUIComponent(window).setActualSize( IUIComponent(window).getExplicitOrMeasuredWidth(), IUIComponent(window).getExplicitOrMeasuredHeight()); } // The layout direction may have changed since last time the // popup was opened so need to reinit mirroring fields. if (FlexVersion.compatibilityVersion >= FlexVersion.VERSION_4_0) { if (window is ILayoutDirectionElement) ILayoutDirectionElement(window).invalidateLayoutDirection(); } if (modal) { // handles accessibility for modal popUps. if(Capabilities.hasAccessibility && Accessibility.active) window.addEventListener(FlexEvent.CREATION_COMPLETE, modalPopUpCreationCompleteHandler, false, 0, true); // create a modal window shield which blocks input and sets up mouseDownOutside logic createModalWindow(parent, o, children, visibleFlag, smp, smp.getSandboxRoot()); } else { o._mouseDownOutsideHandler = nonmodalMouseDownOutsideHandler; o._mouseWheelOutsideHandler = nonmodalMouseWheelOutsideHandler; window.visible = visibleFlag; } // Add show/hide listener so mouse out listeners can be added when // a pop up is shown because applications can be launched and // terminated between the time a pop up is hidden to when it is // shown again. o.owner.addEventListener(FlexEvent.SHOW, showOwnerHandler); o.owner.addEventListener(FlexEvent.HIDE, hideOwnerHandler); addMouseOutEventListeners(o); // Listen for unload so we know to kill the window (and the modalWindow if modal) // this handles _all_ cleanup window.addEventListener(Event.REMOVED, popupRemovedHandler); if (window is IFocusManagerContainer && visibleFlag) { if (hasEventListener("addedPopUp")) { event = new DynamicEvent("addedPopUp", false, true); event.window = window; event.systemManager = smp; dispatchEvent(event); } else awm.activate(IFocusManagerContainer(window)); } // trace("END POPUP: addPopUp" + parent); } mx_internal function getTopLevelSystemManager(parent:DisplayObject):ISystemManager { var localRoot:DisplayObjectContainer; var sm:ISystemManager; if (hasEventListener("topLevelSystemManager")) { var request:Request = new Request("topLevelSystemManager", false, true); request.value = parent; if (!dispatchEvent(request)) localRoot = request.value as DisplayObjectContainer; } if (!localRoot) localRoot = DisplayObjectContainer(parent.root); // If the parent isn't rooted yet, // Or the root is the stage (which is the case in a second AIR window) // use the global system manager instance. if ((!localRoot || localRoot is Stage) && parent is IUIComponent) localRoot = DisplayObjectContainer(IUIComponent(parent).systemManager); if (localRoot is ISystemManager) { sm = ISystemManager(localRoot); if (!sm.isTopLevel()) sm = sm.topLevelSystemManager; } return sm; } /** * Centers a popup window over whatever window was used in the call * to the createPopUp() or addPopUp() method. * *

Note that the position of the popup window may not * change immediately after this call since Flex may wait to measure and layout the * popup window before centering it.

* * @param The IFlexDisplayObject representing the popup. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function centerPopUp(popUp:IFlexDisplayObject):void { if (popUp is IInvalidating) IInvalidating(popUp).validateNow(); const o:PopUpData = findPopupInfoByOwner(popUp); // If we don't find the pop owner or if the owner's parent is not specified or is not on the // stage, then center based on the popUp's current parent. var popUpParent:DisplayObject = (o && o.parent && o.parent.stage) ? o.parent : popUp.parent; if (popUpParent) { var systemManager:ISystemManager = o.systemManager; var x:Number; var y:Number; var appWidth:Number; var appHeight:Number; var parentWidth:Number; var parentHeight:Number; var rect:Rectangle; var clippingOffset:Point = new Point(); var pt:Point; var isTopLevelRoot:Boolean; var sbRoot:DisplayObject = systemManager.getSandboxRoot(); var request:Request; if (hasEventListener("isTopLevelRoot")) { request = new Request("isTopLevelRoot", false, true); } if (request && !dispatchEvent(request)) isTopLevelRoot = Boolean(request.value); else isTopLevelRoot = systemManager.isTopLevelRoot(); if (isTopLevelRoot && (FlexVersion.compatibilityVersion < FlexVersion.VERSION_4_6)) { // The sandbox root is the top level root. // The application width is just the screen width. var screen:Rectangle = systemManager.screen; appWidth = screen.width; appHeight = screen.height; } else { rect = systemManager.getVisibleApplicationRect(); rect.topLeft = DisplayObject(systemManager).globalToLocal(rect.topLeft); rect.bottomRight = DisplayObject(systemManager).globalToLocal(rect.bottomRight); // Offset the top, left of the window to bring it into view. clippingOffset = rect.topLeft.clone(); appWidth = rect.width; appHeight = rect.height; } // If parent is a UIComponent, check for clipping between // the object and its SystemManager if (popUpParent is UIComponent) { rect = UIComponent(popUpParent).getVisibleRect(); if (UIComponent(popUpParent).systemManager != sbRoot) rect = UIComponent(popUpParent).systemManager.getVisibleApplicationRect(rect); var offset:Point = popUpParent.globalToLocal(rect.topLeft); clippingOffset.x += offset.x; clippingOffset.y += offset.y; parentWidth = rect.width; parentHeight = rect.height; } else { parentWidth = popUpParent.width; parentHeight = popUpParent.height; } // The appWidth may smaller than parentWidth if the application is // clipped by the parent application. x = Math.max(0, (Math.min(appWidth, parentWidth) - popUp.width) / 2); y = Math.max(0, (Math.min(appHeight, parentHeight) - popUp.height) / 2); // If the layout has been mirrored, then 0,0 is the uppper // right corner; compensate here. if (FlexVersion.compatibilityVersion >= FlexVersion.VERSION_4_0) { // If popUp has layout direction different than the parent (or parent doesn't // have layout direction and popUp is RTL) flip it to the other side of the x axis. const popUpLDE:ILayoutDirectionElement = popUp as ILayoutDirectionElement; const parentLDE:ILayoutDirectionElement = popUpParent as ILayoutDirectionElement; if (popUpLDE && ((parentLDE && parentLDE.layoutDirection != popUpLDE.layoutDirection) || (!parentLDE && popUpLDE.layoutDirection == LayoutDirection.RTL))) { x = -x /* to flip it on the other side of the x axis*/ -popUp.width /* because 0 is the right edge */; } } pt = new Point(clippingOffset.x, clippingOffset.y); pt = popUpParent.localToGlobal(pt); pt = popUp.parent.globalToLocal(pt); popUp.move(Math.round(x) + pt.x, Math.round(y) + pt.y); } } /** * Removes a popup window popped up by * the createPopUp() or addPopUp() method. * * @param window The IFlexDisplayObject representing the popup window. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function removePopUp(popUp:IFlexDisplayObject):void { // all we want to do here is verify that this popup is one of ours // and remove it from the display list; the REMOVED handler will do the rest // (this is so that we never leak memory, popups will self-manage even if // removePopUp is not called). if (popUp && popUp.parent) { const o:PopUpData = findPopupInfoByOwner(popUp); if (o) { var sm:ISystemManager = o.systemManager; if (!sm) { var iui:IUIComponent = popUp as IUIComponent; // cross-versioning error sometimes returns wrong parent if (iui) sm = ISystemManager(iui.systemManager); else return; } if (o.topMost) sm.popUpChildren.removeChild(DisplayObject(popUp)); else sm.removeChild(DisplayObject(popUp)); } } } /** * Makes sure a popup window is higher than other objects in its child list * The SystemManager does this automatically if the popup is a top level window * and is moused on, * but otherwise you have to take care of this yourself. * * @param The IFlexDisplayObject representing the popup. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function bringToFront(popUp:IFlexDisplayObject):void { if (popUp && popUp.parent) { const o:PopUpData = findPopupInfoByOwner(popUp); if (o) { if (hasEventListener("bringToFront")) { var dynamicEvent:DynamicEvent = new DynamicEvent("bringToFront", false, true); dynamicEvent.popUpData = o; dynamicEvent.popUp = popUp; if (!dispatchEvent(dynamicEvent)) return; } const sm:ISystemManager = ISystemManager(popUp.parent); if (o.topMost) sm.popUpChildren.setChildIndex(DisplayObject(popUp), sm.popUpChildren.numChildren - 1); else sm.setChildIndex(DisplayObject(popUp), sm.numChildren - 1); } } } /** * @private * * Create the modal window. * This is called in two different cases. * 1. Create a modal window for a local pop up. * 2. Create a modal window for a remote pop up. In this case o.owner will be null. */ mx_internal function createModalWindow(parentReference:DisplayObject, o:PopUpData, childrenList:IChildList, visibleFlag:Boolean, sm:ISystemManager, sbRoot:DisplayObject):void { const popup:IFlexDisplayObject = IFlexDisplayObject(o.owner); const popupStyleClient:IStyleClient = popup as IStyleClient; var duration:Number = 0; // Create a modalWindow the size of the stage // that eats all mouse clicks. var modalWindow:Sprite; if (modalWindowClass) { modalWindow = new modalWindowClass(); } else { modalWindow = new FlexSprite(); modalWindow.name = "modalWindow"; } if (!sm && parentReference) sm = IUIComponent(parentReference).systemManager; var awm:IActiveWindowManager = IActiveWindowManager(sm.getImplementation("mx.managers::IActiveWindowManager")); awm.numModalWindows++; // Add it to the collection just below the popup if (popup) childrenList.addChildAt(modalWindow, childrenList.getChildIndex(DisplayObject(popup))); else childrenList.addChild(modalWindow); // force into the automation hierarchy if (popup is IAutomationObject) IAutomationObject(popup).showInAutomationHierarchy = true; o.modalWindow = modalWindow; if (popupStyleClient) modalWindow.alpha = popupStyleClient.getStyle("modalTransparency"); else modalWindow.alpha = 0; modalWindow.tabEnabled = false; const screen:Rectangle = sm.screen; const g:Graphics = modalWindow.graphics; var c:Number = 0xFFFFFF; if (popupStyleClient) { c = popupStyleClient.getStyle("modalTransparencyColor"); } if (hasEventListener("createModalWindow")) { var dynamicEvent:DynamicEvent = new DynamicEvent("createModalWindow", false, true); dynamicEvent.popUpData = o; dynamicEvent.popUp = popup; dynamicEvent.color = c; dynamicEvent.visibleFlag = visibleFlag; dynamicEvent.childrenList = childrenList; if (!dispatchEvent(dynamicEvent)) c = dynamicEvent.color; } // trace("createModalWindow: drawing modal " + s); g.clear(); g.beginFill(c, 100); g.drawRect(screen.x, screen.y, screen.width, screen.height); g.endFill(); if (hasEventListener("updateModalMask")) { dynamicEvent = new DynamicEvent("updateModalMask"); dynamicEvent.popUpData = o; dynamicEvent.popUp = popup; dynamicEvent.childrenList = childrenList; dispatchEvent(dynamicEvent); } // a modal mousedownoutside handler just dispatches the event o._mouseDownOutsideHandler = dispatchMouseDownOutsideEvent; o._mouseWheelOutsideHandler = dispatchMouseWheelOutsideEvent; // the following handlers all get removed in REMOVED on the popup // Set the resize handler so the modal can stay the size of the screen sm.addEventListener(Event.RESIZE, o.resizeHandler); if (popup) { // Listen for show so we know to show the modal window popup.addEventListener(FlexEvent.SHOW, popupShowHandler); // Listen for hide so we know to hide the modal window popup.addEventListener(FlexEvent.HIDE, popupHideHandler); } if (visibleFlag) showModalWindow(o, sm, false); else popup.visible = visibleFlag; if (hasEventListener("createdModalWindow")) { dynamicEvent = new DynamicEvent("createdModalWindow"); dynamicEvent.popUpData = o; dynamicEvent.popUp = popup; dynamicEvent.visibleFlag = visibleFlag; dynamicEvent.childrenList = childrenList; dispatchEvent(dynamicEvent); } } /** * @private */ private function endEffects(o:PopUpData):void { if (o.fade) { o.fade.end(); o.fade = null; } if (o.blur) { o.blur.end(); o.blur = null; } } mx_internal function showModalWindow(o:PopUpData, sm:ISystemManager, sendRequest:Boolean = true):void { const popUpStyleClient:IStyleClient = o.owner as IStyleClient; var duration:Number = 0; var alpha:Number = 0; if (popUpStyleClient) { duration = popUpStyleClient.getStyle("modalTransparencyDuration"); } if (popUpStyleClient) { alpha = popUpStyleClient.getStyle("modalTransparency"); } var blurAmount:Number = 0; if (popUpStyleClient) { blurAmount = popUpStyleClient.getStyle("modalTransparencyBlur"); } var transparencyColor:Number = 0xFFFFFF; if (popUpStyleClient) { transparencyColor = popUpStyleClient.getStyle("modalTransparencyColor"); } var sbRoot:DisplayObject = sm.getSandboxRoot(); if (hasEventListener("showModalWindow")) { var dynamicEvent:DynamicEvent = new DynamicEvent("showModalWindow", false, true); dynamicEvent.popUpData = o; dynamicEvent.sendRequest = sendRequest; dynamicEvent.alpha = alpha; dynamicEvent.blurAmount = blurAmount; dynamicEvent.duration = duration; dynamicEvent.systemManager = sm; dynamicEvent.transparencyColor = transparencyColor; if (!dispatchEvent(dynamicEvent)) { alpha = dynamicEvent.alpha; blurAmount = dynamicEvent.blurAmount; duration = dynamicEvent.duration; transparencyColor = dynamicEvent.transparencyColor; } } o.modalWindow.alpha = alpha; showModalWindowInternal(o, duration, alpha, transparencyColor, blurAmount, sm, sbRoot); } private function setModalPopupVisible(popup:DisplayObject, value:Boolean):void { popup.removeEventListener(FlexEvent.SHOW, popupShowHandler); popup.removeEventListener(FlexEvent.HIDE, popupHideHandler); popup.visible = value; popup.addEventListener(FlexEvent.SHOW, popupShowHandler); popup.addEventListener(FlexEvent.HIDE, popupHideHandler); } /** * @private * Show the modal transparency blocker, playing effects if needed. */ private function showModalWindowInternal(o:PopUpData, transparencyDuration:Number, transparency:Number, transparencyColor:Number, transparencyBlur:Number, sm:ISystemManager, sbRoot:DisplayObject):void { // NO POPUP Data // End any effects that are currently playing for this popup. endEffects(o); if (transparencyDuration) { // Fade effect on the modal transparency blocker const fade:Fade = new Fade(o.modalWindow); fade.alphaFrom = 0; fade.alphaTo = transparency; fade.duration = transparencyDuration; fade.addEventListener(EffectEvent.EFFECT_END, fadeInEffectEndHandler); o.modalWindow.alpha = 0; o.modalWindow.visible = true; o.fade = fade; if (o.owner) setModalPopupVisible(o.owner, false); fade.play(); // Blur effect on the application var blurAmount:Number = transparencyBlur; if (blurAmount) { // Ensure we blur the appropriate top level document. if (DisplayObject(sm).parent is Stage) { // Checking this case first allows WindowedSystemManagers be the blur target. o.blurTarget = sm.document; } else if (sm != sbRoot) { // Get the application document of the sandbox root. // Use a request to get the document so APIs may change // between Flex versions. var sbRootApp:Object; // sbRoot.application; if (hasEventListener("blurTarget")) { var request:Request = new Request("blurTarget", false, true, { popUpData: o }); if (!dispatchEvent(request)) { o.blurTarget = request.value; } } } else o.blurTarget = FlexGlobals.topLevelApplication; const blur:Blur = new Blur(o.blurTarget); blur.blurXFrom = blur.blurYFrom = 0; blur.blurXTo = blur.blurYTo = blurAmount; blur.duration = transparencyDuration; blur.addEventListener(EffectEvent.EFFECT_END, effectEndHandler); o.blur = blur; blur.play(); } } else { if (o.owner) setModalPopupVisible(o.owner, true); o.modalWindow.visible = true; } } /** * @private * Hide the modal transparency blocker, playing effects if needed. * */ mx_internal function hideModalWindow(o:PopUpData, destroy:Boolean = false):void { const popUpStyleClient:IStyleClient = o.owner as IStyleClient; var duration:Number = 0; if (popUpStyleClient) duration = popUpStyleClient.getStyle("modalTransparencyDuration"); // end any effects that are current playing for this popup endEffects(o); if (duration) { // Fade effect on the modal transparency blocker const fade:Fade = new Fade(o.modalWindow); fade.alphaFrom = o.modalWindow.alpha; fade.alphaTo = 0; fade.duration = duration; fade.addEventListener(EffectEvent.EFFECT_END, destroy ? fadeOutDestroyEffectEndHandler : fadeOutCloseEffectEndHandler); o.modalWindow.visible = true; o.fade = fade; fade.play(); // Blur effect on the application const blurAmount:Number = popUpStyleClient.getStyle("modalTransparencyBlur"); if (blurAmount) { const blur:Blur = new Blur(o.blurTarget); blur.blurXFrom = blur.blurYFrom = blurAmount; blur.blurXTo = blur.blurYTo = 0; blur.duration = duration; blur.addEventListener(EffectEvent.EFFECT_END, effectEndHandler); o.blur = blur; blur.play(); } } else { o.modalWindow.visible = false; } if (hasEventListener("hideModalWindow")) { var dynamicEvent:DynamicEvent = new DynamicEvent("hideModalWindow", false, false); dynamicEvent.popUpData = o; dynamicEvent.destroy = destroy; dispatchEvent(dynamicEvent); } } /** * @private * Returns the index position of the PopUpData in the popupInfo array (or -1) * for a given popupInfo.owner */ private function findPopupInfoIndexByOwner(owner:Object):int { const n:int = popupInfo.length; for (var i:int = 0; i < n; i++) { var o:PopUpData = popupInfo[i]; if (o.owner == owner) return i; } return -1; } /** * @private * Returns the PopUpData (or null) for a given popupInfo.owner */ private function findPopupInfoByOwner(owner:Object):PopUpData { var index:int = findPopupInfoIndexByOwner(owner); return index > -1 ? popupInfo[index] : null; } /** * @private * Add mouse out listeners for modal and non-modal windows. */ private function addMouseOutEventListeners(o:PopUpData):void { var sbRoot:DisplayObject = o.systemManager.getSandboxRoot(); if (o.modalWindow) { o.modalWindow.addEventListener(MouseEvent.MOUSE_DOWN, o.mouseDownOutsideHandler); o.modalWindow.addEventListener(MouseEvent.MOUSE_WHEEL, o.mouseWheelOutsideHandler, true); } else { sbRoot.addEventListener(MouseEvent.MOUSE_DOWN, o.mouseDownOutsideHandler); sbRoot.addEventListener(MouseEvent.MOUSE_WHEEL, o.mouseWheelOutsideHandler, true); } if (hasEventListener("addMouseOutEventListeners")) { var dynamicEvent:DynamicEvent = new DynamicEvent("addMouseOutEventListeners", false, false); dynamicEvent.popUpData = o; dispatchEvent(dynamicEvent); } } /** * @private * Remove mouse out listeners for modal and non-modal windows. */ private function removeMouseOutEventListeners(o:PopUpData):void { var sbRoot:DisplayObject = o.systemManager.getSandboxRoot(); if (o.modalWindow) { o.modalWindow.removeEventListener(MouseEvent.MOUSE_DOWN, o.mouseDownOutsideHandler); o.modalWindow.removeEventListener(MouseEvent.MOUSE_WHEEL, o.mouseWheelOutsideHandler, true); } else { sbRoot.removeEventListener(MouseEvent.MOUSE_DOWN, o.mouseDownOutsideHandler); sbRoot.removeEventListener(MouseEvent.MOUSE_WHEEL, o.mouseWheelOutsideHandler, true); } if (hasEventListener("removeMouseOutEventListeners")) { var dynamicEvent:DynamicEvent = new DynamicEvent("removeMouseOutEventListeners", false, false); dynamicEvent.popUpData = o; dispatchEvent(dynamicEvent); } } //-------------------------------------------------------------------------- // // Event handlers // //-------------------------------------------------------------------------- /** * @private * Set by PopUpManager on modal windows so they show when the parent shows. */ private function popupShowHandler(event:FlexEvent):void { const o:PopUpData = findPopupInfoByOwner(event.target); if (o) showModalWindow(o, getTopLevelSystemManager(o.parent)); } /** * @private * Set by PopUpManager on modal windows so they hide when the parent hide */ private function popupHideHandler(event:FlexEvent):void { const o:PopUpData = findPopupInfoByOwner(event.target); if (o) hideModalWindow(o); } /** * @private */ private function showOwnerHandler(event:FlexEvent):void { const o:PopUpData = findPopupInfoByOwner(event.target); if (o) { // add mouse out listeners. addMouseOutEventListeners(o); } } /** * @private */ private function hideOwnerHandler(event:FlexEvent):void { const o:PopUpData = findPopupInfoByOwner(event.target); if (o) { // remove mouse out listeners removeMouseOutEventListeners(o); } } /** * @private * Set by PopUpManager on modal windows to monitor when the parent window gets killed. * PopUps self-manage their memory -- when they are removed using removePopUp OR * manually removed with removeChild, they will clean themselves up when they leave the * display list (including all references to PopUpManager). */ private function popupRemovedHandler(event:Event):void { const n:int = popupInfo.length; for (var i:int = 0; i < n; i++) { var o:PopUpData = popupInfo[i], popUp:DisplayObject = o.owner; if (popUp == event.target) { var popUpParent:DisplayObject = o.parent, modalWindow:DisplayObject = o.modalWindow, sm:ISystemManager = o.systemManager; if (!sm.isTopLevel()) sm = sm.topLevelSystemManager; if (popUp is IUIComponent) IUIComponent(popUp).isPopUp = false; var awm:IActiveWindowManager = IActiveWindowManager(sm.getImplementation("mx.managers::IActiveWindowManager")); if (popUp is IFocusManagerContainer) { awm.removeFocusManager(IFocusManagerContainer(popUp)); } popUp.removeEventListener(Event.REMOVED, popupRemovedHandler); if (hasEventListener("removeMouseOutEventListeners")) { var event2:DynamicEvent = new DynamicEvent("popUpRemoved"); event2.popUpData = o; dispatchEvent(event2); } if (o.owner) { o.owner.removeEventListener(FlexEvent.SHOW, showOwnerHandler); o.owner.removeEventListener(FlexEvent.HIDE, hideOwnerHandler); } removeMouseOutEventListeners(o); // modal if (modalWindow) { // restore accessibility to document removeModalPopUpAccessibility(popUp); // clean up all handlers sm.removeEventListener(Event.RESIZE, o.resizeHandler); popUp.removeEventListener(FlexEvent.SHOW, popupShowHandler); popUp.removeEventListener(FlexEvent.HIDE, popupHideHandler); hideModalWindow(o, true); awm.numModalWindows--; } popupInfo.splice(i, 1); break; } } } /** * @private * Show the modal window after the fade effect finishes */ private function fadeInEffectEndHandler(event:EffectEvent):void { effectEndHandler(event); const n:int = popupInfo.length; for (var i:int = 0; i < n; i++) { var o:PopUpData = popupInfo[i]; if (o.owner && o.modalWindow == event.effectInstance.target) { setModalPopupVisible(o.owner, true); break; } } } /** * @private * Remove the modal window after the fade effect finishes */ private function fadeOutDestroyEffectEndHandler(event:EffectEvent):void { effectEndHandler(event); const obj:DisplayObject = DisplayObject(event.effectInstance.target); var modalMask:DisplayObject = obj.mask; if (modalMask) { // modal mask is always added to the popupChildren list. obj.mask = null; sm.popUpChildren.removeChild(modalMask); } if (obj.parent is ISystemManager) { const sm:ISystemManager = ISystemManager(obj.parent) if (sm.popUpChildren.contains(obj)) sm.popUpChildren.removeChild(obj); else sm.removeChild(obj); } else { if (obj.parent) // Mustella can already take you off stage obj.parent.removeChild(obj); } } /** * @private * Remove the modal window after the fade effect finishes */ private function fadeOutCloseEffectEndHandler(event:EffectEvent):void { effectEndHandler(event); DisplayObject(event.effectInstance.target).visible = false; } /** * @private */ private function effectEndHandler(event:EffectEvent):void { const n:int = popupInfo.length; for (var i:int = 0; i < n; i++) { var o:PopUpData = popupInfo[i]; var e:IEffect = event.effectInstance.effect; if (e == o.fade) o.fade = null; else if (e == o.blur) o.blur = null; } } /** * @private * If not modal, use this kind of mouseDownOutside logic */ private static function nonmodalMouseDownOutsideHandler(owner:DisplayObject, evt:MouseEvent):void { // shapeFlag is false here for performance reasons if (owner.hitTestPoint(evt.stageX, evt.stageY, true)) { } else { if (owner is IUIComponent) if (IUIComponent(owner).owns(DisplayObject(evt.target))) return; dispatchMouseDownOutsideEvent(owner, evt); } } /** * @private * If not modal, use this kind of mouseWheelOutside logic */ private static function nonmodalMouseWheelOutsideHandler(owner:DisplayObject, evt:MouseEvent):void { // shapeFlag is false here for performance reasons if (owner.hitTestPoint(evt.stageX, evt.stageY, true)) { } else { if (owner is IUIComponent) if (IUIComponent(owner).owns(DisplayObject(evt.target))) return; dispatchMouseWheelOutsideEvent(owner, evt); } } /** * @private * This mouseWheelOutside handler just dispatches the event. */ private static function dispatchMouseWheelOutsideEvent(owner:DisplayObject, evt:MouseEvent):void { // this is a modal window without a popup owner to dispatch the message to. if (!owner) return; const event:MouseEvent = new FlexMouseEvent(FlexMouseEvent.MOUSE_WHEEL_OUTSIDE); const pt:Point = owner.globalToLocal(new Point(evt.stageX, evt.stageY)); event.localX = pt.x; event.localY = pt.y; event.buttonDown = evt.buttonDown; event.shiftKey = evt.shiftKey; event.altKey = evt.altKey; event.ctrlKey = evt.ctrlKey; event.delta = evt.delta; event.relatedObject = InteractiveObject(evt.target); owner.dispatchEvent(event); } /** * @private * This mouseDownOutside handler just dispatches the event. */ private static function dispatchMouseDownOutsideEvent(owner:DisplayObject, evt:MouseEvent):void { // this is a modal window without a popup owner to dispatch the message to. if (!owner) return; const event:MouseEvent = new FlexMouseEvent(FlexMouseEvent.MOUSE_DOWN_OUTSIDE); const pt:Point = owner.globalToLocal(new Point(evt.stageX, evt.stageY)); event.localX = pt.x; event.localY = pt.y; event.buttonDown = evt.buttonDown; event.shiftKey = evt.shiftKey; event.altKey = evt.altKey; event.ctrlKey = evt.ctrlKey; event.delta = evt.delta; event.relatedObject = InteractiveObject(evt.target); owner.dispatchEvent(event); } /** * @private * This method handles the creation of a modal popUp */ private function modalPopUpCreationCompleteHandler(event:FlexEvent):void { event.target.removeEventListener(FlexEvent.CREATION_COMPLETE, modalPopUpCreationCompleteHandler); addModalPopUpAccessibility(event.currentTarget as DisplayObject); } /** * @private * This method handles the creation of a modal popUp when assistive * technology is active, by silencing the content of the top-level document. */ private function addModalPopUpAccessibility(popUp:DisplayObject):Boolean { if (Capabilities.hasAccessibility && Accessibility.active) { const p:PopUpData = findPopupInfoByOwner(popUp); if (p) { const n:int = popupInfo.length; for (var i:int = 0; i < n; i++) { var o:PopUpData = popupInfo[i]; if (o && o != p) { o.owner.accessibilityProperties.silent = true; } } var sbRoot:Object = p.systemManager.getSandboxRoot(); // getTopLevelSystemManager(p.parent); if (!sbRoot.document.accessibilityProperties) sbRoot.document.accessibilityProperties = new AccessibilityProperties(); // This hides top-level document content from assistive technology. sbRoot.document.accessibilityProperties.silent = true; Accessibility.updateProperties(); } return true; } return false; } /** * @private * This method handles the removal of a modal popUp when assistive technology is active, * by exposing the content of the top-level document. * * @return */ private function removeModalPopUpAccessibility(popUp:DisplayObject):Boolean { if (Capabilities.hasAccessibility && Accessibility.active) { const p:PopUpData = findPopupInfoByOwner(popUp); if (p) { handleAccessibilityForNestedPopups(popUp); if (popupInfo.length<=1) { var sbRoot:Object = p.systemManager.getSandboxRoot(); sbRoot.document.accessibilityProperties.silent = false; } Accessibility.updateProperties(); } return true; } return false; } /** * @private * This method handles accessibility for nested popUps. * If this method is called from outside removeModalPopUpAccessibility, * it is mandatory to invoke Accessibility.updateProperties() to communicate * the individual popup's accessibilityProperties change to the screen reader. * *

This method should only come into play with modal popUps, in which case it will * either expose accessibility of a modal popUp underneath the popUp being closed * without exposing the underlying content, or continue exposing accessibility * of popUps until there are no more to expose.

*/ private function handleAccessibilityForNestedPopups(popUpBeingVisited:DisplayObject):void { if (!popUpBeingVisited) return; var index:int = findPopupInfoIndexByOwner(popUpBeingVisited); var popupData:PopUpData = index > -1 ? popupInfo[index] : null; var underneathPopUpData:PopUpData; if (index == 0) { var sm:ISystemManager = getTopLevelSystemManager(popupData.parent); if (sm) { // If this is the only popUp, we should expose accessibility // of the top-level system manager's document sm.document.accessibilityProperties.silent = false; // We should also expose accessibility // of the sandbox root's document. var sbRoot:Object = popupData.systemManager.getSandboxRoot(); sbRoot.document.accessibilityProperties.silent = false; } } else if (index > 0) { // If more than one popUp is open, we should expose accessibility // of the underlying popUp underneathPopUpData = popupInfo[index - 1]; underneathPopUpData.owner.accessibilityProperties.silent = false; // All the nested popUp's AccessibilityProperties // changes should be handled by a single // Accessibility.updateProperties // call in removeModalPopUpAccessibility. // If the underlying popUp is modal, we should stop recursing. if (underneathPopUpData.modalWindow) return; handleAccessibilityForNestedPopups(underneathPopUpData.owner); } } } }