/*
** Copyright (c) 2008, 2020, Oracle and/or its affiliates. 
*/

/**
 * AdfDhtmlCommonFlashPeer a subclass of AdfRichUIPeer
 * @extends AdfRichUIPeer
 */

// common flash peer
//BUG FIX #10162106: support for QTP automation for HV - 
//don't override peer definition if it's already been defined, because
//additional automation support may have already been added to existing
//definition
if (typeof AdfDhtmlCommonFlashPeer == 'undefined')
{
    AdfRichUIPeer.createPeerClass(AdfRichUIPeer, "AdfDhtmlCommonFlashPeer");
}

AdfDhtmlCommonFlashPeer.InitSubclass = function () {
  AdfObject.ensureClassInitialization(AdfDhtmlCommonFlashPeer);
  //BUG FIX #7372699: listen for focus and blur events so we know when
  //the flash window should have focus
  AdfRichUIPeer.addComponentEventHandlers(this, 
    AdfComponentEvent.FOCUS_EVENT_TYPE, AdfComponentEvent.BLUR_EVENT_TYPE);

  if (!AdfDhtmlCommonFlashPeer.isTouchDevice()) {
    AdfRichUIPeer.addComponentEventHandlers(this, AdfUIInputEvent.MOUSE_DOWN_EVENT_TYPE);
  }
  
  AdfRichUIPeer.addSuppressedPPRAttributes(this, AdfDvtComponent.POPUP_STYLE);
}

//TODO: Derive from javax.faces.SEPARATOR_CHAR context param in JSF 2
AdfDhtmlCommonFlashPeer.NAMING_SEPARATOR = ":";

AdfDhtmlCommonFlashPeer.LOAD_XML_INITIAL = "initial";

AdfDhtmlCommonFlashPeer.IS_FULLY_SCROLLED = 1;
AdfDhtmlCommonFlashPeer.IS_PARTIALLY_SCROLLED = 2;

AdfDhtmlCommonFlashPeer.__IS_IN_TABLE_KEY = "isInTable";
AdfDhtmlCommonFlashPeer.__IS_IN_MASHUP_KEY = "isInMashup";

/**
 * @export
 * Callback from Flash to change the bComponentLoaded flag
 */
AdfDhtmlCommonFlashPeer.setComponentLoaded = function (clientId, bLoaded)
{
  var component = AdfPage.PAGE.findComponent(clientId);   
  var peer = component.getPeer();
  peer.bComponentLoaded = (bLoaded == "true");
  // If clicks are not received automatically by the dom due to clicks in Flash, set javascript to call on click
  if (peer.IsImageFormatFlash(component) && peer.IsAutomationMode(component) && peer.FlashClicksUnavailable()) {
    var movieObj = component.getFlashMovieObj();
    movieObj['setAutomation']("AdfDhtmlCommonFlashPeer.handleAutomationClick");
  }
}


AdfDhtmlCommonFlashPeer.prototype.FlashClicksUnavailable = function()
{
    var platform = AdfAgent.getAgent().getPlatform();
    var isChrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1;
    return (platform == AdfAgent.IE_PLATFORM || isChrome);
}

AdfDhtmlCommonFlashPeer.prototype.IsAutomationMode = function(component)
{
  return component.getProperty("isAutomation");
}

/**
 * @export
 */
AdfDhtmlCommonFlashPeer.handleAutomationClick = function (clientId) {
   // Create and fire click event
   var component = AdfPage.PAGE.findComponent(clientId);   

   var peer = component.getPeer();
   if (peer.IsImageFormatFlash(component) && peer.IsAutomationMode(component) && peer.FlashClicksUnavailable()) {
     var movieObj = component.getFlashMovieObj();
     if (document.createEventObject) { //Internet Explorer
        var event = document.createEventObject();
        movieObj['fireEvent']('onclick', event);
     } else if (document.createEvent) { // Other browsers
        var event = document.createEvent("MouseEvents");
        event.initMouseEvent("click", true, true, document.defaultView, 1, peer._currentScreenX, peer._currentScreenY, peer._currentClientX, peer._currentClientY, false, false, false, false, 0, null);
        movieObj['dispatchEvent'](event);
     }
   }

}

/**
 * Displays the data uri in attachment mode.
 */
AdfDhtmlCommonFlashPeer.prototype.DisplayDataUri = function(component) {
  // Find the container of the image
  var clientId = component.getClientId();
  var imgContainer = AdfAgent.AGENT.getElementById(AdfRichUIPeer.CreateSubId(clientId, "mainDiv"));
  var img = AdfAgent.AGENT.getElementById(AdfRichUIPeer.CreateSubId(clientId, "img"));
  var uriSize = Number(imgContainer.getAttribute("_urisize"));

  // Get and data URI and strip the prefix. The URI is sent as an XML comment in the first child of the imgContainer.
  var uriContainer = imgContainer.firstChild;
  var uri = uriContainer.innerHTML;
  var index1 = uri.indexOf('<!--');
  var index2 = uri.lastIndexOf('-->');
  uri = uri.substring(index1 + 4, index2);
  
  if(AdfDhtmlCommonFlashUtils.isDataURISupported(uriSize)) {   
    // Use the data URI directly
    img.src = uri;
  }
  
  // Finally display the image
  imgContainer.style.display = "inline";
}

/**
 * Returns the image format that the component should render in.
 * @param component
 * @return {string} The private string constant indicating the image format.
 */
AdfDhtmlCommonFlashPeer.prototype.getClientImageFormat = function(component) {
  // A component may be provided if the peer isn't associated yet
  if(!component)
    component = this.getComponent();

  var imageFormat = component.getProperty("clientImageFormat");
  if(imageFormat == "ATTACHMENT") {
    // Check for HTML5 Support and return HTML5 if available
    if(AdfDhtmlCommonFlashUtils.isHTML5Supported())
      return "HTML5";
    else
      return "ATTACHMENT"; // This is the signal to use PNG images
  }
  else
    return imageFormat;
}

/**
 * Determine if the imageFormat is FLASH.
 * 
 * @param component component associated with this peer
 * 
 * @return true if the imageView is to be rendered in Flash
 *         false otherwise
 */
AdfDhtmlCommonFlashPeer.prototype.IsImageFormatFlash = function(component)
{
  // default false, but subclasses should override
  return false;
}

/**
 * Determine if the imageFormat is screen reader.
 * 
 * @param component component associated with this peer
 * 
 * @return true if the component is to be rendered in screen reader mode
 *         false otherwise
 */
AdfDhtmlCommonFlashPeer.prototype.IsImageFormatScreenReader = function(component)
{
  return AdfPage.PAGE.isScreenReaderMode();
}

/**
 * Hook into ADF page ready state
 */
AdfDhtmlCommonFlashPeer.prototype.GetReadyState = function()
{
// BUG FIX 11686254 - if flash component is not visible, do not have
// it block as a loading component


  if(this.isScrolledIntoView()) {
    if (this.bComponentLoaded) return AdfUIComponent.STATE_COMPLETE;
    else return AdfUIComponent.STATE_LOADING;
  } else { 
    return AdfUIComponent.STATE_COMPLETE;
  }
  
}

// BUG FIX 11686254
/**
 * Determine if this component is currently visible in the browser
 */
AdfDhtmlCommonFlashPeer.prototype.isScrolledIntoView = function(scrollType) {

  // TODO: we are not doing anything with the scrollType at the moment
  if(scrollType == undefined)
    scrollType = AdfDhtmlCommonFlashPeer.IS_PARTIALLY_SCROLLED;
    

  var element = this.getDomElement();

  var isVisibleAndDisplayed = this.isVisibleAndDisplayed(element);
  if(!isVisibleAndDisplayed) {
    AdfLogger.LOGGER.fine("peer for ", this.getDomElement().id, ", isScrolledIntoView returns false");
    return false;
  }

  var viewport = this.GetViewport(element);
  // check if our dom element is in this viewport
  var isInViewport = false;
  
  if(viewport != null) {
    isInViewport = this.IsInViewport(viewport, element);
  }
  
  AdfLogger.LOGGER.fine("peer for ", this.getDomElement().id, ", isScrolledIntoView returns ", isInViewport);
  return isInViewport;
}

/*
 * This method returns an object that describes the bounds of the browser-visible container in which this 
 * element is located.  The method takes a top-down approach by getting the size of the browser viewport 
 * and then gradually cutting this viewport down based on the size of the scrollable ancestors of this element.
 * 
 * The method returns null if the dom element is in a container that is not visible in the browser window.
 */
AdfDhtmlCommonFlashPeer.prototype.GetViewport = function(element) {
  AdfAssert.assertDomNode(element);

  var scrollTop;
  var scrollLeft;
  var windowWidth;
  var windowHeight;

  if(element.nodeName == "BODY") {
  
//    scrollTop = document.documentElement.scrollTop;
//    scrollLeft = document.documentElement.scrollLeft;
//    windowWidth = document.documentElement.clientWidth;
//    windowHeight = document.documentElement.clientHeight;

    // get the current page bounds of the browser window  
    scrollTop = AdfAgent.AGENT.getBrowserViewportScrollTop();
    scrollLeft = AdfAgent.AGENT.getBrowserViewportScrollLeft();
    windowWidth = AdfAgent.AGENT.getWindowClientWidth(AdfAgent.AGENT.getDomWindow());
    windowHeight = AdfAgent.AGENT.getWindowClientHeight(AdfAgent.AGENT.getDomWindow());
    
    return {top:scrollTop,
          left:scrollLeft,
          bottom:(scrollTop + windowHeight),
          right:(scrollLeft + windowWidth)};
  }
  
  var parentElement = element.parentNode;
  
  var isParentVisible = this.isVisibleAndDisplayed(parentElement);
  if(!isParentVisible)
    return null;
  
  var viewport = this.GetViewport(parentElement);
  
  var isScrollable = false;
  
  // check if we are a scrollable component
  // TODO: check for absolutely positioned elements - see Agent.isClipped for an example
  if(viewport != null) {
    var hasScrollableHeight = (element.scrollHeight - element.offsetHeight) > 0 ? true : false;
    var hasScrollableWidth = (element.scrollWidth - element.offsetWidth) > 0 ? true :false;
    if(hasScrollableWidth || hasScrollableHeight)
        isScrollable = true;
  }
  
  // clip the viewport only if we are scollable
  if(isScrollable) {

    var isInViewport = this.IsInViewport(viewport, element);
    
    if(isInViewport) {
    
    // if the current element is a scrollable component and is located in the current viewport,
    // trim the viewport down to how much of the current element is shown in the current viewport
      
        var currentBounds = AdfAgent.AGENT.getElementPageBounds(element);
        var top = currentBounds.top;
        var bottom = currentBounds.bottom;
        var left = currentBounds.left;
        var right = currentBounds.right;
      
        var vTop = viewport.top;
        var vBottom = viewport.bottom;
        var vLeft = viewport.left;
        var vRight = viewport.right      
      
        var maxLeft;
        var minRight;
        var maxTop;
        var minBottom;
          
        (vLeft - left > 0) ? maxLeft = vLeft : maxLeft = left;
        (vRight - right > 0) ? minRight = right : minRight = vRight;
        (vTop - top > 0) ? maxTop = vTop : maxTop = top;
        (vBottom - bottom > 0) ? minBottom = bottom : minBottom = vBottom;
                    
        viewport = {top:maxTop,
                    left:maxLeft,
                    bottom:minBottom,
                    right:minRight};
          
      } else {
          viewport = null;
      }
  }
  return viewport;
}

AdfDhtmlCommonFlashPeer.prototype.IsInViewport = function(viewport, element) {
    AdfAssert.assertDomNode(element);
    
    // if the viewport is null, then that means the dom element's parent
    // container is not visible in the browser window
    if(viewport == null)
        return false;

    var currentBounds = AdfAgent.AGENT.getElementPageBounds(element);
    var top = currentBounds.top;
    var bottom = currentBounds.bottom;
    var left = currentBounds.left;
    var right = currentBounds.right;
      
    var vTop = viewport.top;
    var vBottom = viewport.bottom;
    var vLeft = viewport.left;
    var vRight = viewport.right
            
    var isInViewport = !( (bottom <= vTop) || (top >= vBottom) || (left >= vRight) || (right <= vLeft));
    return isInViewport;    
}


AdfDhtmlCommonFlashPeer.prototype.isVisibleAndDisplayed = function(element) {
    if (element == null)
        return false;
    
    var compStyle = AdfAgent.AGENT.getComputedStyle(element); 
    var isDisplayed = (compStyle.display != "none");    
    var isVisible =  (compStyle.visibility != "hidden") && (element.style.visibility != "collapse");    
    return isDisplayed && isVisible;
}


// End BUG FIX 11686254

AdfDhtmlCommonFlashPeer.GetSwfId = function (clientId)
{
  var swfId = AdfDhtmlCommonFlashPeer.replaceString(clientId + "_swf", AdfDhtmlCommonFlashPeer.NAMING_SEPARATOR, "_");
  swfId = AdfDhtmlCommonFlashPeer.replaceString(swfId, AdfRichUIPeer.CreateSubId("", ""), "_");
  return swfId;
}

/**
 * Find the absolute position on the page of the given element.
 * 
 * @return an object defining the x and y coords of the element
 */ 
AdfDhtmlCommonFlashPeer.FindPos = function(obj)
{
  var currLeft = 0;
  var currTop = 0;
  
  while (obj.offsetParent != null)
  {
    currLeft += obj.offsetLeft;
    currTop += obj.offsetTop;
    
    //these are defined in IE
    if (obj.clientLeft != null)
      currLeft += obj.clientLeft;
    if (obj.clientTop != null)
      currTop += obj.clientTop;
    
    obj = obj.offsetParent;
  }
  
  //these are defined in IE
  if (obj.clientLeft != null)
    currLeft += obj.clientLeft;
  if (obj.clientTop != null)
    currTop += obj.clientTop;
  
  return {x: currLeft, y: currTop};
}

/**
 * Convert popup alignment as specified in the af:showPopupBehavior
 * tag to an AdfRichPopup constant.  
 * 
 * @param align popup alignment from the jspx
 * 
 * @return AdfRichPopup alignment constant
 */ 
AdfDhtmlCommonFlashPeer.convertPopupAlignToConstant = function(align)
{
  switch (align)
  {
    case "afterStart":
      return AdfRichPopup.ALIGN_AFTER_START;
    case "afterEnd":
      return AdfRichPopup.ALIGN_AFTER_END;
    case "beforeStart":
      return AdfRichPopup.ALIGN_BEFORE_START;
    case "beforeEnd":
      return AdfRichPopup.ALIGN_BEFORE_END;
    case "endAfter":
      return AdfRichPopup.ALIGN_END_AFTER;
    case "endBefore":
      return AdfRichPopup.ALIGN_END_BEFORE;
    case "startAfter":
      return AdfRichPopup.ALIGN_START_AFTER;
    case "startBefore":
      return AdfRichPopup.ALIGN_START_BEFORE;
    case "overlap":
      return AdfRichPopup.ALIGN_OVERLAP;
    default:
      return null;
  }
}

/**
 * @export
 * Initialize the associated domElement when the peer is created.  Stateless peers
 * are not bound at the time that this method is called.
 * @param {AdfUIComponent} component Component that this peer will be associated with
 * @param {Object} domElement Root DOM element of this peer
 */
AdfDhtmlCommonFlashPeer.prototype.InitDomElement = function(component, domElement)
{
  AdfDhtmlCommonFlashPeer.superclass.InitDomElement.call(this, component, domElement);

  AdfDhtmlCommonFlashPeer.checkComponentImageFailAndPPR(component, domElement);
  
  this._checkForHighContrastInBrowser();
  
  //BUG FIX #7372699: maintain flags for focus changes
  this.bHandlingComponentFocus = false;
  this.bTransferringFocus = false;

  if (component.getProperty("clientImageFormat") == "FLASH"
      || component.getProperty("clientRenderFormat") == "flash")
  {
    // Register component so that page will call GetReadyState
    AdfPage.PAGE.registerLoadingComponent(component);
  }
}

/**
 * Borrowed from AdfDhtmlPage._checkForHighContrastInBrowser which sets the high contrast mode in AdfDhtmlPage which isn't
 * called until after our components render and loads the private AfHighContrast class which could be obfuscated so we
 * copied over the logic into our own method.
 * @private
 */
AdfDhtmlCommonFlashPeer.prototype._checkForHighContrastInBrowser = function() {
  // Only preform these checks if high contrast setting is available on dvt.Agent which 
  // isn't the case for Graph and if it's not already set
  // Bug 22755394- Adding check that dvt is defined, since this line broke graph after namespace changes.  
  if (typeof dvt != 'undefined' && typeof dvt.Agent == 'function' && dvt.Agent.isHighContrast && !dvt.Agent.isHighContrast()) {
    var div = document.createElement('div');
    div.style.border = "1px solid";
    div.style.borderColor = "red green";
    div.style.position = "absolute";
    div.style.height = "5px";
    div.style.top = "-999px";
    div.style.backgroundImage = "url('" + AdfPage.PAGE.getResourceURL("/afr/blank.gif") + "')";
    document.body.appendChild(div);
    var cs = window.getComputedStyle(div);
    var bki = cs.backgroundImage;
    if ((cs.borderTopColor == cs.borderRightColor) || (bki && (bki == 'none' || bki == 'url (invalid-url:)'))) {
      dvt.Agent.setHighContrast(true);
    }
    document.body.removeChild(div);
  }
}

/**
 * @export
 * Unregister the component from the ready state
 */
AdfDhtmlCommonFlashPeer.prototype.ComponentRemoved = function(component)
{
  AdfDhtmlCommonFlashPeer.superclass.ComponentRemoved.call(this, component);
  
  // Fix for 14582626: If the component is removed, explicitly hide the tooltip.  This is
  // needed for ppr navigation or anything that removes the component without causing a mouseOut to occur.
  if(this._view && this._view.getCtx && this._view.getCtx()) {
    this.HideComponentTooltips();
  }

  if (component.getProperty("clientImageFormat") == "FLASH"
      || component.getProperty("clientRenderFormat") == "flash")
  {
    AdfPage.PAGE.unregisterLoadingComponent(component);
  }
}

AdfDhtmlCommonFlashPeer.prototype.HideComponentTooltips = function() {
  this._view.getCtx().hideTooltips();
}

AdfDhtmlCommonFlashPeer.isComponentImageCheckNeeded = function(component) {
    // Only check for image fail if this was a full request
    var isFullRender = component.getProperty("isFullRender");
    return (isFullRender && isFullRender == "true");
}

AdfDhtmlCommonFlashPeer.checkComponentImageFailAndPPR = function(component, domElement) {
  // Components opt in for this behavior by sending the relevant client properties in the renderer
  if (!AdfDhtmlCommonFlashPeer.isComponentImageCheckNeeded(component)) {
    return;
  }
  if (component && domElement) {
      var imgElems = domElement.getElementsByTagName("img");
      if (imgElems) {
          for (var i=0;i<imgElems.length;i++) {
            var imgElem = imgElems[i];
            // if any of the images fail, ppr the component
            if (AdfDhtmlCommonFlashPeer.checkImageFail(imgElem, component)) {
                AdfPage.PAGE.addPartialTargets(component);
                break;
            }
          }
      }
  }
}

AdfDhtmlCommonFlashPeer.checkImageFail = function(imgElem, component) {
  if (imgElem && AdfDhtmlCommonFlashPeer.isImageLoadingFailed(imgElem)) {
    return true;
  }
  return false;
}
  
AdfDhtmlCommonFlashPeer.isImageLoadingFailed = function(img) {
    AdfAssert.assert(img, "Image is null");
    return (!img.complete || (typeof img.naturalWidth != undefined && img.naturalWidth == 0));
}

/**
 * @export
 * Transfer focus from the flash window towards the previous component on the page.  
 * 
 * @param clientId clientId of the component
 */
AdfDhtmlCommonFlashPeer.transferFocusPrev = function(clientId)
{
  //in IE, don't need to do anything here because IE will handle the focus
  //change itself
  if (AdfAgent.getAgent().getPlatform() == AdfAgent.IE_PLATFORM)
    return;
  
  var comp = AdfPage.PAGE.findComponent(clientId);
  if (comp != null)
  {
    var peer = comp.getPeer();
    if (peer != null)
    {
      //set the flag indicating we're currently changing focus
      peer.bTransferringFocus = true;
      
      //lose focus on the flash window
      peer.HandleComponentBlur(null);
      //focus the root dom element
      peer.getDomElement().focus();
      
      //clear the flag now that the transfer is done
      peer.bTransferringFocus = false;
    }
  }
}

/**
 * @export
 * Transfer focus from the flash window towards the next component on the page.  
 * 
 * @param clientId clientId of the component
 */
AdfDhtmlCommonFlashPeer.transferFocusNext = function(clientId)
{
  //in IE, don't need to do anything here because IE will handle the focus
  //change itself
  if (AdfAgent.getAgent().getPlatform() == AdfAgent.IE_PLATFORM)
    return;
  
  var comp = AdfPage.PAGE.findComponent(clientId);
  if (comp != null)
  {
    var peer = comp.getPeer();
    if (peer != null)
    {
      //set the flag indicating we're currently changing focus
      peer.bTransferringFocus = true;
      
      //lose focus on the flash window
      peer.HandleComponentBlur(null);
      
      //clear the flag now that the transfer is done
      peer.bTransferringFocus = false;
    }
  }
}

/**
 * Handle a mouse down event on the component.
 * 
 * @param e event object
 */
AdfDhtmlCommonFlashPeer.prototype.HandleComponentMouseDown = function (e) {

    // BUGFIX 13409877
    // check if the mouse down will cause the component to receive focus
    if(this.isComponentFocused)
    {
      if(!this.isComponentFocused() && this.setComponentFocusedViaMouse)
      {
        this.setComponentFocusedViaMouse(true);
      }
    }


    var agent = AdfAgent.AGENT;
    if (agent.getPlatform() == AdfAgent.GECKO_PLATFORM) {
        var nativeEvent = e.getNativeEvent();
        // Bug #12661959: Blue border appears in firefox on mouse down when the ctrl key is pressed when inside <td>
        if (nativeEvent.ctrlKey)
            AdfAgent.AGENT.preventDefault(nativeEvent);
    }
}

/**
 * Handle a focus event on the component.
 * 
 * @param e event object
 */
AdfDhtmlCommonFlashPeer.prototype.HandleComponentFocus = function(e)
{
  // BUGFIX 13657323
  // avoid handling back-to-back blur/focus events because one of this peer's subcomponents 
  // e.g., a context menu, got focus
  // If we have a pending blur in a timeout clear it
  if(this._blurTimeout)
  {
    clearTimeout(this._blurTimeout);
    this._blurTimeout = null;
  }

  // BUGFIX 13409877
  // record the fact that this component has focus; 
  // used to determine if the mouse down caused component to receive focus
  if(this.setComponentFocused)
  {
    this.setComponentFocused(true);
  }

  if (!this.NeedsComponentFocusListeners())
    return;
  
  //only handle the event if we're not already in the middle of a focus
  //change (for example, focusing the root element again after tabbing
  //out of the flash window)
  if (!this.bTransferringFocus)
  {
    //set the flag indicating we're handling a focus event
    this.bHandlingComponentFocus = true;
    
    var movieObj = this.getMovieObject(this.getComponent());
    if (movieObj != null && movieObj['setFocused'] != null)
    {
      //show focus in flash
      
      if(this.isComponentFocusedViaMouse)
      {
        // BUGFIX 13409877; only apply visual effects if we did not receive focus via mouse 
        // (i.e., we received focus by tabbing in)
        // if we gain focus via mouse, then the click will apply visual effects for us
        if(!this.isComponentFocusedViaMouse())
        {
          movieObj['setFocused'](true);
        }        
      }
      else 
      {
        movieObj['setFocused'](true);
      }

      //give focus to the flash window if not in IE (IE automatically
      //gives focus to the flash window)
      if (AdfAgent.getAgent().getPlatform() != AdfAgent.IE_PLATFORM)
        movieObj['focus']();
    }
    
    // if HTML5, have the peer set focus
    else if (this.getComponent().getProperty("clientImageFormat") == "HTML5")
    {
      if(this.isComponentFocusedViaMouse)
      {
        // BUGFIX 13409877; only apply visual effects if we did not receive focus via mouse 
        // (i.e., we received focus by tabbing in)
        // if we gain focus via mouse, then the click will apply visual effects for us   
        if(!this.isComponentFocusedViaMouse())
        {
          this.SetFocused(true);
        }
      }
      else
      {
        this.SetFocused(true);
      }
    }
    
    //clear the flag now that we're done handling the event
    this.bHandlingComponentFocus = false;
  }
}

/**
 * Handle a blur event on the component.
 * 
 * @param e event object
 */
AdfDhtmlCommonFlashPeer.prototype.HandleComponentBlur = function(e)
{
  // BUGFIX 13657323
  // avoid back-to-back blur/focus because one of this peer's subcomponents got focus
  // e.g., when a context menu is triggered. this is patterned after how AdfDhtmlTableBasePeer handles blur and focus events
  if(!this._blurTimeoutCallback)
    this._blurTimeoutCallback = this.createCallback(this._doBlurTimeout);
  this._blurTimeout = setTimeout(this._blurTimeoutCallback, 10);
}

/**
 * Callback function that will be called shortly after receiving a blur event. This method will clear
 * the keyboard focus effect from the component. However, this method will not be called if a focus event
 * is received before the blur timeout expires.  We need this behavior to handle context menus; whenever
 * a user interacts with the context menu, the peer receives back-to-back blur/focus events.
 * This pattern of using a timer to work around the back-to-back blur/focus events caused by context menus
 * is taken from AdfDhtmlTableBasePeer.
 */
AdfDhtmlCommonFlashPeer.prototype._doBlurTimeout = function()
{
  // BUGFIX 13409877
  // record the fact that this component has lost focus; 
  // used to determine if a mouse down caused component to receive focus
  if(this.setComponentFocused)
  {
    this.setComponentFocused(false);
  }
  if(this.setComponentFocusedViaMouse)
  {
    this.setComponentFocusedViaMouse(false);
  }

  if (!this.NeedsComponentFocusListeners())
    return;
  
  //only handle the event if we're not already in the middle of a focus event
  //(for example, when the component first gets focus and we transfer it to the
  //flash window, the component will immediately get a blur event because
  //it lost focus)
  if (!this.bHandlingComponentFocus)
  {
    var movieObj = this.getMovieObject(this.getComponent());
    if (movieObj != null && movieObj['setFocused'] != null)
    {
      //hide focus in flash
      movieObj['setFocused'](false);
      //remove focus from the flash window if not in IE (IE automatically
      //removes focus from the flash window)
      if (AdfAgent.getAgent().getPlatform() != AdfAgent.IE_PLATFORM)
        movieObj['blur']();
    }

    // if not flash, have the peer set focus
    else if (this.getComponent().getProperty("clientImageFormat") == "HTML5")
    {
      this.SetFocused(false);
    }    
  }
}

/**
 * Perform any component specific actions when gaining or losing keyboard focus.
 * 
 * @param boolean isFocused
 * 
 */
AdfDhtmlCommonFlashPeer.prototype.SetFocused = function(isFocused)
{
  if(this._view && this._view.setFocused)
    this._view.setFocused(isFocused);
}

AdfDhtmlCommonFlashPeer.prototype.isValidVariable = function (testVar) {
  if (null == testVar || undefined == testVar)
    return false;
 return true;
}

AdfDhtmlCommonFlashPeer.prototype.getStatusDivId = function () {
  return "status";
}

AdfDhtmlCommonFlashPeer._getChildById = function(parent, id) {
  var nodes = parent.childNodes;
  for (var i=0; i < nodes.length; i++) {
    if (id == nodes[i].id)
      return nodes[i];
  }
  return null;
}

/**
 * @export
 */
AdfDhtmlCommonFlashPeer.prototype.needsResizeNotify = function(component) {
  return true;
}
/*
AdfDhtmlCommonFlashPeer.prototype.ResizeNotify = function(
  oldWidth, oldHeight, newWidth, newHeight)
{
}
*/

/**
 * Retrieves the Flash movie object.  This is the only supported function to retrieve
 * access to the Flash movie.
 */
AdfDhtmlCommonFlashPeer.prototype.getFlashMovieObj = function(component) {
  // For convenience, we will try to look up the component if not provided
  if(component == null)
    component = this.getComponent();
    
  // Find the movie name  
  var movieName = AdfDhtmlCommonFlashPeer.GetSwfId(component.getClientId());
 
  // Then retrieve the actual swf
  var movieObj;
  if (navigator.appName.indexOf("Microsoft") != -1)
  {
    if (window[movieName])
      movieObj = window[movieName];
    else
      movieObj = document[movieName];
  }
  else
  {
    movieObj = document[movieName] ;
  }
 
  // Fix for bug 9361445: After certain DOM moves, the Flash Player's External Interface
  // callbacks may be lost.  If this is the case, loadXML will no longer be registered and
  // we will need to reload the component.  We check specifically that _reloadEventSent
  // is false.  Once the Flash Player is loaded, we set _reloadEventSent to false.  If we 
  // check before it's loaded, it will simply be undefined and no event will be sent.
  // Fix for bug 10194999: When a splitter with 2 graphs is collapsed, the visible graph
  // will be resized, causing a server event and busyStateListener to be called.  The 
  // collapsed graph will now be hidden with display:none, causing all its js hooks to be
  // null.  We don't want to send the reload event in this case.
  if(movieObj && !movieObj['loadXML'] && this._reloadEventSent == false && this._inBusyStateListener != true) { 
    var event = new AdfCommonFlashReloadEvent(component);
    event.setPartial(true);
    AdfPage.PAGE.queueEventToServer(event);
    // Only send a single reload event.  The reload causes the component and peer to be
    // replaced, which will reset the flag.
    this._reloadEventSent = true;
  } 
 
  return movieObj;
}

// TODO remove getMovieObject and call getFlashMovieObj
AdfDhtmlCommonFlashPeer.prototype.getMovieObject = function (component) {
  return this.getFlashMovieObj(component);
}

AdfDhtmlCommonFlashPeer.prototype.getDataXml = function (clientId) {
  return AdfDhtmlCommonFlashPeer._getDataXml(clientId);
}

/**
 * Returns the string from a comment div
 * @param {String} clientId The component clientId used in the comment div id
 * @param {String} divId The div id that is appended to the component clientId
 * @return {String} 
 * @protected
 */
AdfDhtmlCommonFlashPeer.GetXmlComment = function (clientId, divId) {
  var dataDiv = document.getElementById(clientId + divId);
  return AdfDhtmlCommonFlashPeer.Trim(dataDiv);
}

/**
 * Returns the string from a comment div
 * @param {HTMLElement} dataDiv The div element to retrieve and trim the inner HTML of
 * @return {String} 
 * @protected
 */
AdfDhtmlCommonFlashPeer.Trim = function (dataDiv) {
  if (dataDiv) {
    var dataString = dataDiv.innerHTML;
    var index1 = dataString.indexOf('<!--');
    var index2 = dataString.lastIndexOf('-->');
    dataString = dataString.substring(index1 + 4, index2);
    return dataString;
  }
  return null;
}

/**
 * Determine if the desired Flash Player version is available.  
 * When called with reqMajorVer, reqMinorVer, reqRevision returns true if that 
 * version or greater is available.
 * 
 * @param reqMajorVer major version number
 * @param reqMinorVer minor version number
 * @param reqRevision revision number
 * 
 * @return true if version is available, false if not
 */
AdfDhtmlCommonFlashPeer.prototype.isFlashVersion = function (reqMajorVer, reqMinorVer, reqRevision) {
  // TODO remove all calls to this, use the common utils directly
  return AdfDhtmlCommonFlashUtils.isFlashVersion(reqMajorVer, reqMinorVer, reqRevision);
}

/**
 * Determine if the flash player tag has been added to the DOM.
 * 
 * @param component component associated with this peer
 * 
 * @return true if the flash tag has already been added to the DOM,
 *         false otherwise
 */
AdfDhtmlCommonFlashPeer.prototype.isFlashPlayerAddedToDom = function (component) {
  var clientId = component.getClientId();
  var flashPlayerId = AdfRichUIPeer.CreateSubId(clientId, this.getFlashPlayerDivId());
  var flashTag = AdfAgent.AGENT.getElementById(flashPlayerId);
  var tagHTML = flashTag.innerHTML;
  var index1 = tagHTML.indexOf('<!--');
  var index2 = tagHTML.lastIndexOf('-->');
  if (index1 >  - 1 && index2 >  - 1)
    return false;

  return true;  
}

AdfDhtmlCommonFlashPeer.prototype.getFlashPlayerDivId = function () {
  return "flashplayer";
}

/**
 * Show the message indicating that the user should download the latest
 * Flash Player.  
 * 
 * @param component the client-side component
 */
AdfDhtmlCommonFlashPeer.prototype.showFlashPlayerDownloadMessage = function (component) {
  var tagId = AdfRichUIPeer.CreateSubId(component.getClientId(), this.getFlashPlayerUnavailableDivId());
  var flashTag = AdfAgent.AGENT.getElementById(tagId);
  if (flashTag)
    flashTag.style['display'] = 'block';
}

AdfDhtmlCommonFlashPeer.prototype.getFlashPlayerUnavailableDivId = function () {
  return "getflashplayer";
}

/**
 * Add the <object> or <embed> flash tag to the DOM.
 * 
 * @param component component associated with this peer
 */
AdfDhtmlCommonFlashPeer.prototype.addFlashPlayerToDom = function (component) {
  if (!AdfDhtmlCommonFlashUtils.isMinFlashVersion())
  {
    this.showFlashPlayerDownloadMessage(component);
    return;
  }

  var clientId = component.getClientId();

  /*
  var bIsInPopup = component.getProperty('isInPopup');
  if (bIsInPopup && (bIsInPopup === true) && (AdfAgent.getAgent().getPlatform() != AdfAgent.IE_PLATFORM) && (component.getContentDelivery() == "immediate"))
    window.setTimeout(AdfDhtmlCommonFlashPeer.uncommentFlashTag, 75, flashTagId);
*/
  var flashPlayerId = AdfRichUIPeer.CreateSubId(clientId, this.getFlashPlayerDivId());
  AdfDhtmlCommonFlashPeer.uncommentFlashTag(flashPlayerId, component);
  
  if (this.NeedsMouseScrollListener())
    this.AddMouseScrollListener(component);
}

AdfDhtmlCommonFlashPeer.prototype.NeedsMouseScrollListener = function ()
{
  return true;
}

AdfDhtmlCommonFlashPeer.prototype.NeedsComponentFocusListeners = function ()
{
  return true;
}

//BUG FIX #6979623: 
//BUG FIX #9215709: 
/**
 * Add a mouse scroll listener to the domElement.
 * 
 * @param component the component associated with this peer
 */
AdfDhtmlCommonFlashPeer.prototype.AddMouseScrollListener = function (component)
{
  var domElement = this.getDomElement();
  if (domElement != null)
  {
    if (domElement.addEventListener != null)
    {
      //Firefox
      domElement.addEventListener('DOMMouseScroll', this.HandleMouseScroll, false);
      //Opera
      domElement.addEventListener('mousewheel', this.HandleMouseScroll, false);
    }
    else if (domElement.attachEvent != null)
    {
      //IE
      domElement.attachEvent('onmousewheel', this.HandleMouseScroll);
    }
  }
}

/**
 * @export
 * Handle a mouse scroll event.
 * 
 * @param e event object
 */
AdfDhtmlCommonFlashPeer.prototype.HandleMouseScroll = function(e)
{
  //when this function is called, "this" refers to the component's dom element,
  //so need to find the actual component and peer objects based on the dom
  //element's id
  //BUG FIX #9215709: in IE, "this" refers to the window object, so need to
  //find the actual component and peer objects based on the srcElement of 
  //the event
  var component;
  if (this.id != null)
    component = AdfPage.PAGE.findComponent(this.id);
  if (component == null)
  {
    if (e.target != null)
      component = AdfRichUIPeer.getFirstAncestorComponent(e.target);
    else if (e.srcElement != null)
      component = AdfRichUIPeer.getFirstAncestorComponent(e.srcElement);
  }
  if (component == null)
    return;
  
  var peer = component.getPeer();
  if (peer == null)
    return;
  
  var movieObj = peer.getMovieObject(component);
  if (movieObj != null)
  {
    //BUG FIX #6979623: in Firefox, when wmode='transparent' or wmode='opaque' on 
    //the <embed> tag, Flash doesn't receive mouse wheel events, so workaround 
    //that by listening to DOM mouse wheel events in javascript and passing them 
    //into Flash
    //BUG FIX #9215709: AS will dispatch mouseWheel event for all browsers
    //when we call isMouseWheelHandled() in order to avoid scrolling
    //the browser page at the same time
    /*if (AdfAgent.getAgent().getPlatform() == AdfAgent.GECKO_PLATFORM)
    {
      var wmodeAttr = movieObj.attributes.wmode;
      if (wmodeAttr != null && 
          (wmodeAttr.value == 'transparent' || wmodeAttr.value == 'opaque'))
      {
        peer.MozMouseScroll(e, movieObj);
      }
    }*/
    
    //BUG FIX #9215709: if the component handled the mouse wheel event,
    //then prevent the event from affecting the browser scrollbar
    if (movieObj['isMouseWheelHandled'] != null)
    {
      //BUG FIX #9943565: get the mouse position and element position
      //relative to the document and then subtract to get the mouse
      //position relative to the flash window      
      var mousePos = AdfAgent.getAgent().getMousePosition(e);
      var elemPos = AdfAgent.getAgent().getElementPosition(movieObj);
      var xCoord = mousePos.x - elemPos.x;
      var yCoord = mousePos.y - elemPos.y;
      
      var delta;
      //check for wheelDelta first, because Safari and Chrome both it
      //and detail, but they set detail = 0
      if (e.wheelDelta != null)
        delta = e.wheelDelta / 40;
      else if (e.detail != null)
      {
        //need to reverse the sign of the delta so that mouse wheel up zooms in
        //and mouse wheel down zooms out
        delta = -e.detail;
      }
      
      var bMouseWheelHandled = movieObj['isMouseWheelHandled'](delta, xCoord, yCoord);
      if (bMouseWheelHandled == true)
        AdfDhtmlCommonFlashPeer.CancelDomEvent(e);
    }
  }
}

/**
 * Cancel a DOM event.
 * 
 * @param e event object to cancel
 */
AdfDhtmlCommonFlashPeer.CancelDomEvent = function(e)
{
  if (e == null)
    e = window.event;
  if (e.stopPropagation != null)
    e.stopPropagation();
  if (e.preventDefault != null)
    e.preventDefault();
  e.cancelBubble = true;
  e.cancel = true;
  e.returnValue = false;
}

//BUG FIX #6979623: 
/**
 * Handle a mouse wheel event on the dom element for this component when
 * in Firefox.
 * 
 * @param e mouse wheel event
 * @param movieObj flash object
 */
/*AdfDhtmlCommonFlashPeer.prototype.MozMouseScroll = function(e, movieObj)
{
  //need to reverse the sign of the delta so that mouse wheel up zooms in
  //and mouse wheel down zooms out
  var delta = -e.detail;
  var mouseX = e.layerX;
  var mouseY = e.layerY;
  if (movieObj.firefoxMouseWheel != null)
    movieObj.firefoxMouseWheel(delta, mouseX, mouseY);
}*/

AdfDhtmlCommonFlashPeer.prototype.getParamValue = function(searchParam, params, paramValues) {
  if (!this.isValidVariable(searchParam)) 
    return null;
  
  var index = null;
  for (i = 0; i <= params.length; i++) {
    var curParam = params[i];
    if (curParam == searchParam) {
      index = i;
      break;
    }
  }
  
  if (!this.isValidVariable(index) || index >= paramValues.length)
    return null;
    
  return paramValues[index];
}


/**
 * @export
 */
AdfDhtmlCommonFlashPeer.prototype.createShowPopupBehaviorEvent = function(component, params, paramValues) {
  return new AdfCommonFlashShowPopupBehaviorEvent(component, 
              this.getParamValue("clientRowKey", params, paramValues),
              this.getParamValue("popupId", params, paramValues),             
              this.getParamValue("alignId", params, paramValues),             
              this.getParamValue("align", params, paramValues),             
              this.getParamValue("mouseStageX", params, paramValues),             
              this.getParamValue("mouseStageY", params, paramValues),             
              this.getParamValue("compStageX", params, paramValues),             
              this.getParamValue("compStageY", params, paramValues),             
              this.getParamValue("compStageWidth", params, paramValues),             
              this.getParamValue("compStageHeight", params, paramValues),
              this.getParamValue("bAlignCompInFlash", params, paramValues),
              this.getParamValue("launchingComponentId", params, paramValues)
             );  
}

/**
 * Get the client id of the popup placement helper element.
 * 
 * @return client id
 */
AdfDhtmlCommonFlashPeer.prototype.getPopupPlacementElemId = function()
{
  var component = this.getComponent();
  var clientId = component.getClientId();
  var elemId = AdfRichUIPeer.CreateSubId(clientId, "popupHelper");
  return elemId;
}


/**
 * Get the popup placement helper element used to align popups to
 * stamped components in the SWF.
 * 
 * @return popup placement helper element
 */
AdfDhtmlCommonFlashPeer.prototype.GetPopupPlacementElem = function()
{
  var elemId = this.getPopupPlacementElemId();
  var popupHelperElem = AdfAgent.AGENT.getElementById(elemId);
  
  return popupHelperElem;
}

/**
 * Position the popup placement helper element to the location of the
 * triggering Flash component.  
 * 
 * @param x coord of component in Flash stage
 * @param y coord of component in Flash stage
 * @param width width of component in Flash stage
 * @param height height of component in Flash stage
 */
AdfDhtmlCommonFlashPeer.prototype.positionPopupPlacementElem = function(x, y, width, height)
{
  var popupHelperElem = this.GetPopupPlacementElem();
  if (popupHelperElem != null)
  {
    if(x == undefined) 
      x = 0;
    if(y == undefined)
      y = 0;
    if(width == undefined)
      width = 1;
    if(height == undefined)
      height = 1;
    
    popupHelperElem.style.left = x + "px";
    popupHelperElem.style.top = y + "px";
    popupHelperElem.style.width = width + "px";
    popupHelperElem.style.height = height + "px";
    //set hidden element to be temporarily visible for popup alignment
    popupHelperElem.style.display = "inline";
    this.ForceSetPropertyChange(AdfDvtComponent.POPUP_STYLE, popupHelperElem.style.cssText, false, AdfUIComponent.PROPAGATE_ALWAYS);
  }
}

/**
 * Hide the popup placement helper element.  
 */
AdfDhtmlCommonFlashPeer.prototype.HidePopupPlacementElem = function()
{
  var popupHelperElem = this.GetPopupPlacementElem();
  if (popupHelperElem != null)
    popupHelperElem.style.display = "none";
}

/**
 * @protected
 * Handle the event when a popup is opening.
 * @param {AdfPopupOpeningEvent} evt The popup-opening event
 */
AdfDhtmlCommonFlashPeer.prototype.HandlePopupOpening = function(evt) {
  //FIX BUG 16390019: make sure the popup placement elem is shown when the popup opens
  var popupHelperElem = this.GetPopupPlacementElem();
  if (popupHelperElem != null)
    popupHelperElem.style.display = "inline";
  
  //FIX BUG 17184467: remove event listener after current thread completes to avoid interfering with code keeping track of current listeners
  var popup = evt.getSource();
  var thisRef = this;
  window.setTimeout(function() {popup.removeEventListener(AdfPopupOpeningEvent.POPUP_OPENING_EVENT_TYPE, thisRef.HandlePopupOpening, thisRef)}, 0);
}

/**
 * Handle the event when a popup opens.
 * 
 * @param popup-opened event
 */
//FIX BUG #10158372: wait until popup is closed to hide placement element
//so that popup aligns correctly; this function is no longer called
//from here, but may still be called from AdfDhtmlDvtImageViewPeer
AdfDhtmlCommonFlashPeer.prototype.handlePopupOpened = function(evt)
{
  //hide the alignment helper element
  this.HidePopupPlacementElem();
  //remove the popup-opened listener
  var popup = evt.getSource();
  popup.removeEventListener(AdfPopupOpenedEvent.POPUP_OPENED_EVENT_TYPE,
                            this.handlePopupOpened, this);
}

/**
 * Handle the event when a popup closes.
 * 
 * @param popup-opened event
 */
AdfDhtmlCommonFlashPeer.prototype.handlePopupClosed = function(evt)
{
  //remove the popup-closed listener
  var popup = evt.getSource();
  popup.removeEventListener(AdfPopupClosedEvent.POPUP_CLOSED_EVENT_TYPE,
                            this.handlePopupClosed, this);

  //FIX BUG #10158372: wait until popup is closed to hide placement element
  //so that popup aligns correctly
  //hide the alignment helper element
  this.HidePopupPlacementElem();
  
  var component = this.getComponent();  
  var movieObj = this.getMovieObject(component);
  
  //BUG FIX #10079489: maintain flag for when a popup is shown with triggerType
  //mouseHover so that we don't keep sending out events to try to show it when
  //it's already being shown
  if (movieObj != null && movieObj['setHoverPopupShown'] != null) 
    movieObj['setHoverPopupShown'](component.getClientId(), false); 
}

AdfDhtmlCommonFlashPeer.prototype.showPopupHelper = function(clientRowKey)
{
  //do nothing; subclasses must override to do something useful
}

/**
 * @export
 * @param clientRowKey row key associated with triggering component
 * @param popupId id of the popup to show
 * @param alignId id of the component to align the popup to (not currently used)
 * @param align alignment attribute as specified in the jspx af:showPopupBehavior tag
 * @param triggerType type of gesture that triggered the popup
 * @param mouseStageX x coord of mouse in Flash stage (not currently used)
 * @param mouseStageY y coord of mouse in Flash stage (not currently used)
 * @param compStageX x coord of triggering component in Flash stage
 * @param compStageY y coord of triggering component in Flash stage
 * @param compStageWidth width of triggering component in Flash stage
 * @param compStageHeight height of triggering component in Flash stage
 * @param bAlignCompInFlash true if the aligning component is in Flash,
 *        false if not
 * @param launchingComponentId client id of the popup launch source
 * @param cliendId (optional) client id of the popup component peer if it is not the current peer
*/ 
AdfDhtmlCommonFlashPeer.prototype.showPopup = function(clientRowKey, 
                                    popupId, alignId, align, triggerType, mouseStageX, mouseStageY,
                                    compStageX, compStageY, compStageWidth, compStageHeight,
                                    bAlignCompInFlash, launchingComponentId, clientId) {
  if (AdfPage.PAGE.isScreenReaderMode())
    return;
    
  var component;
  if (clientId)
    component = AdfPage.PAGE.findComponent(clientId);
  else
    component = this.getComponent();
  var popup = AdfPage.PAGE.findComponent(popupId);

  
  //BUG FIX #7253885: use the component's findComponent method to search
  //for the popup by id, because the clientId may contain concatenated naming 
  //container ids, which we don't know about
  if (popup == null)
    popup = component.findComponent(popupId);
  if (popup != null) {
  
    //BUG FIX #9946388: cleanup of any existing popups before trying to load
    //a new one
    popup.hide();
    this.HidePopupPlacementElem();
    //FIX BUG #10158372: wait until popup is closed to hide placement element
    //so that popup aligns correctly
    //popup.removeEventListener(AdfPopupOpenedEvent.POPUP_OPENED_EVENT_TYPE,
    //                    this.handlePopupOpened, this);
    popup.removeEventListener(AdfPopupClosedEvent.POPUP_CLOSED_EVENT_TYPE,
                        this.handlePopupClosed, this);
  
    component.getPeer().showPopupHelper(clientRowKey);
      
    var hints = new Array();
    
    if(!launchingComponentId){
      //BUG FIX 8427586: pass the HV clientId as the launch source to the popup,
      //so that the HV can be referred to in EL for the popup with an 
      //af:setPropertyListener tag
      launchingComponentId = component.getClientId();
    }
    
    hints[AdfRichPopup.HINT_LAUNCH_ID] = launchingComponentId;

    var bMouseHover = (triggerType == "mouseHover");
    
    //BUG FIX #7517663: if an alignId is specified, and if the launching component
    //is in the HV node (in Flash), then position the popup placement helper
    //element and use its id instead to align the popup; otherwise just let
    //ADF Faces use the original alignId to find the component on the page
    //outside of HV
    //Update 3/26/10: If aligning in Flash, allow null alignId
    if (alignId != null || bAlignCompInFlash)
    {
      var popupHelperId = alignId;
      if (bAlignCompInFlash)
      {
        popupHelperId = this.getPopupPlacementElemId();
        this.positionPopupPlacementElem(compStageX, compStageY, compStageWidth, compStageHeight);
        //FIX BUG 16390019: make sure the placement elem is shown in order to align the popup (in case it gets
        //hidden before the new popup is shown due to an asynchronous close event from the previous popup)
        popup.addEventListener(AdfPopupOpeningEvent.POPUP_OPENING_EVENT_TYPE,
                               this.HandlePopupOpening, this);
      }
      
      var popupAlign = AdfDhtmlCommonFlashPeer.convertPopupAlignToConstant(align);
      if (popupAlign != null && popupHelperId != null) {
        hints[AdfRichPopup.HINT_ALIGN_ID] = popupHelperId;
        hints[AdfRichPopup.HINT_ALIGN] = popupAlign;
      }

      //FIX BUG #10158372: wait until popup is closed to hide placement element
      //so that popup aligns correctly
      /*if (bAlignCompInFlash)
      {
        //add a listener for the popup-opened event so we can hide the popup
        //alignment helper element
        popup.addEventListener(AdfPopupOpenedEvent.POPUP_OPENED_EVENT_TYPE,
                               this.handlePopupOpened, this);
      }*/
    }
    
    //FIX BUG #10158372: wait until popup is closed to hide placement element
    //so that popup aligns correctly
    if ((alignId != null && bAlignCompInFlash) ||
        bMouseHover)
    {
      //add a listener for the popup-closed event so we can hide the popup
      //alignment helper element after the popup closes, because we'll need
      //it for the duration of the popup
      popup.addEventListener(AdfPopupClosedEvent.POPUP_CLOSED_EVENT_TYPE,
                             this.handlePopupClosed, this);
    }
    
    //BUG FIX #10079489: for mouseHover popup, let AF auto dismissal hide it
    
    // when using the mouseHover hybrid event, let the auto dismissal manager handle dismissing
    // the popup on mouse out.
    if (bMouseHover)
    {
      hints[AdfDhtmlCommonUtils.AUTODISMISS] = AdfDhtmlCommonUtils.AUTODISMISS_MOUSEOUT;
      //BUG FIX 15851577: break out the autodismiss mouseout id into a separate function so that
      //components (e.g. Diagram and HTML5 HV) can override
      var mouseOutId = this.GetPopupMouseOutId(hints);
      hints[AdfDhtmlCommonUtils.AUTODISMISS_MOUSEOUT_ID] = mouseOutId;

      var movieObj = this.getMovieObject(component);
      
      //BUG FIX #10079489: maintain flag for when a popup is shown with triggerType
      //mouseHover so that we don't keep sending out events to try to show it when
      //it's already being shown, because that can result in it flickering
      if (movieObj != null && movieObj['setHoverPopupShown'] != null) 
        movieObj['setHoverPopupShown'](component.getClientId(), true); 
    }
    //This is a Dvt ShowPopup event
    hints['isDvtShowPopup'] = true;
    popup.show(hints);
  }
}

/**
 * @protected
 * Pre-process show popup behaviors
 * This function must be called only for Top-level DVT Components(like dvt.Diagram, DvtHierarchyViewer) and not necessary for sub-components.
 * 
 * Issue: If we have Show Popup Behavior defined for Top-level DVT Components and also on Sub components, 
 * 1. Every mouse action on sub-component displays 2 popups, one for Top-level component and one for sub-component.
 * 2. On touch devices, we are seeing one popup on touch start and another popup on touch end.
 * 
 * Reason:  The primary reason for above issue is DVT displays a popup and Adf displays another popup.
 * Adf recognizes the mouse/touch events on all Top-level DVT Components.
 * For each and every Touch or Mouse event on Top-level components, DVT needs to consume/handle the event and stop the event from propagating.
 * If we don't handle that event, the event will get bubbled up to Adf and Adf has its own event handler for Touch and Mouse Events.
 * This issue happens only for Top-level components, because Adf has its own 'client listeners' assigned for Top-level components
 * Adf ignores the events on sub-component because it couldn't recognize them (sub-component event target)
 * 
 * Solution: Attach an 'Popup Opening Event' Handler for each Popup in Top-level DVT Component.  This Handler gets triggered before the Popup is displayed.
 * And we will be able to check if this event is from DVT or from Adf.  
 * This helps to validate the event before the displaying the popup.
 * 
 * @param {Array} spbs  Array of show popup behaviors
 * 
 */
AdfDhtmlCommonFlashPeer.prototype.PreprocessShowPopupBehaviors = function(spbs) {
  if(this.getComponent() && spbs) {
    for(var idx=0; idx< spbs.length; idx++) {
      var popup = this.getComponent().findComponent(spbs[idx].getPopupId());
      if(popup) {
        popup.addEventListener(AdfPopupOpeningEvent.POPUP_OPENING_EVENT_TYPE,
                             this.ProcessPopupOpeningEvent, this);
      }
    }
  }
};

/**
 * @protected
 * Handle the event when a popup is opening.
 * Only Dvt generated popup opening event will have the hint 'isDvtShowPopup' 
 * Adf generated Popup opening event will not have this hint.
 * 
 * @param {AdfPopupOpeningEvent} event The popup-opening event
 */
AdfDhtmlCommonFlashPeer.prototype.ProcessPopupOpeningEvent = function(event) {
  var hints = event.getHints();
  if(!hints['isDvtShowPopup']) {
    //Canceling the event will stop further processing of event in Adf
    //This will prevent the popup from displaying.
    event.cancel(); 
  }
};


/**
 * @protected
 */
AdfDhtmlCommonFlashPeer.prototype.GetPopupMouseOutId = function(hints) {
  var mouseOutId = hints[AdfRichPopup.HINT_ALIGN_ID]
                     ? hints[AdfRichPopup.HINT_ALIGN_ID]
                     : hints[AdfRichPopup.HINT_LAUNCH_ID];
  return mouseOutId;
}

/**
 * @export
 */
AdfDhtmlCommonFlashPeer.fpInitNotify = function( clientId ) {
  // Init param for tracking the reload event.  This is set to true when we request a new
  // component from the server in order to fix bug 9584433, where the Flash ExternalInterface
  // callbacks are disconnected after a DOM move.
  var component = AdfPage.PAGE.findComponent(clientId);
  if(component) {
    var peer = component.getPeer();
    peer._reloadEventSent = false;  
  }

  //BUG FIX #11060989: update the size of the component in Flash
  //so that we can differentiate between a normal resize and a 
  //browser zoom
  AdfDhtmlCommonFlashPeer.updateSize(component, component.getPeer()._clientWidth, component.getPeer()._clientHeight);
  return AdfDhtmlCommonFlashPeer._getDataXml(clientId);
}

/**
 * @export
 */
AdfDhtmlCommonFlashPeer.fpMessage = function( msg ) {
  
}

//BUG FIX #11060989: update the size of the component in Flash
//so that we can differentiate between a normal resize and a 
//browser zoom
/**
 * Update the size of the component in Flash.
 * 
 * @param component the JS component
 * @param newWidth new component width
 * @param newHeight new component height
 */
AdfDhtmlCommonFlashPeer.updateSize = function(component, newWidth, newHeight) {
  //BUG FIX #12563653: don't try to compare JS component size and
  //Flash stage size to determine browser zoom, because in Firefox
  //4 it results in every resize being handled like a browser zoom;
  //revert to old behavior before bug 11060989
  /*if (component) {
      var movieObj = component.getPeer().getFlashMovieObj(component);
      if (movieObj && movieObj.updateSize) {
        movieObj.updateSize(newWidth, newHeight);
        return true;
      }  
  }*/
  return false;
}

AdfDhtmlCommonFlashPeer._getDataXml = function(clientId) {
  var newClientId = AdfDhtmlCommonFlashPeer.replaceString(clientId, ":", "_");
  var dataXml = document.getElementById(newClientId + "content");
  var xmlText = dataXml.innerHTML;

  var index1 = xmlText.indexOf('<!--');
  var index2 = xmlText.lastIndexOf('-->');
  xmlText = xmlText.substring(index1 + 4, index2);
  return xmlText;
}

/*
 * BEGIN - STATIC FLASH HELPER API
 */

/**
 * Uncomment the flash tag.
 * 
 * @param flashTagId id of the element containing the commented-out flash tag
 */
AdfDhtmlCommonFlashPeer.uncommentFlashTag = function (flashTagId, component) {
  var flashTag = AdfAgent.AGENT.getElementById(flashTagId);
  if (flashTag) {
    var tagHTML = flashTag.innerHTML;

    var index1 = tagHTML.indexOf('<!--');
    var index2 = tagHTML.lastIndexOf('-->');
    if (index1 >= 0 && index2 > index1)
      tagHTML = tagHTML.substring(index1 + 4, index2);
    flashTag.innerHTML = tagHTML;
    
    // Bug 12925682 Restore the title of the document in IE
    if(AdfAgent.getAgent().getPlatform() == AdfAgent.IE_PLATFORM && component!=null) {
      var bIsInPortlet = component.getProperty('isInPortlet');
      if(!bIsInPortlet)
        AdfDhtmlCommonFlashUtils.restoreTitle(component.getClientId());
    }
  }
}

AdfDhtmlCommonFlashPeer.replaceString = function (sourceString, searchString, replaceString) {
  var ret = new String(sourceString);
  var regExp = new RegExp(searchString, "g");
  ret = ret.replace(regExp, replaceString);
  return ret;
}

/**
 * @export
 * Get the resource URL for the given URL.
 * This function is called from Flash when loading images.
 * 
 * @param origUrl original URL
 * 
 * @return resource URL
 */
AdfDhtmlCommonFlashPeer.getResourceURL = function(origUrl)
{
  /*
  if (origUrl != null)
  {
    // Note: In case of url came from the blaf css file, 
    // it is in absolute url path, no need to add additional context root
    var contextRoot = AdfPage.PAGE.getResourceURL("/");
    if (origUrl.indexOf(contextRoot) == 0)
      return origUrl;

    if (origUrl.indexOf("://") >= 0)
      origUrl = '/mapproxy?rtarget=' + origUrl;      
  }

  var encodedUrl = AdfPage.PAGE.getResourceURL(origUrl);

  return encodedUrl;
  */

  return origUrl;
}

//function dvt_flash_queueEvent(clientId, eventType, params, paramValues)
//{
//  AdfDhtmlCommonFlashPeer.queueFlashEvent(clientId, eventType, params, paramValues);
//}

/**
 * @export
 */
AdfDhtmlCommonFlashPeer.queueFlashEvent = function(clientId, eventType, params, paramValues)
{
  var component = AdfPage.PAGE.findComponent(clientId);   
  var peer = component.getPeer();
  peer.queueEvent(eventType, params, paramValues);
}

/**
 * @export
 */
AdfDhtmlCommonFlashPeer.prototype.queueEvent = function(eventType, params, paramValues)
{
  var evalEventType = eval(eventType);

  var bHandled = false; 
  if (evalEventType == AdfCommonFlashShowPopupBehaviorEvent.SHOW_POPUP_BEHAVIOR_EVENT)
  {
    // invoking the popup directly instead of raising the showpopup event
      this.showPopup(
            this.getParamValue("clientRowKey", params, paramValues),
            this.getParamValue("popupId", params, paramValues),             
            this.getParamValue("alignId", params, paramValues),             
            this.getParamValue("align", params, paramValues),             
            this.getParamValue("triggerType", params, paramValues),             
            this.getParamValue("mouseStageX", params, paramValues),             
            this.getParamValue("mouseStageY", params, paramValues),             
            this.getParamValue("compStageX", params, paramValues),             
            this.getParamValue("compStageY", params, paramValues),             
            this.getParamValue("compStageWidth", params, paramValues),             
            this.getParamValue("compStageHeight", params, paramValues),
            this.getParamValue("bAlignCompInFlash", params, paramValues),
            this.getParamValue("launchingComponentId", params, paramValues));
    
    //event = peer.createShowPopupBehaviorEvent(component, params, paramValues);
    bHandled = true;
  }
  return bHandled;
}

//BUG FIX #8714175: force the property change to propagate back to the
//middle tier, even if the value being set is the same as the old value
/**
 * Set a property on the peer's component and force a property change.
 */
AdfDhtmlCommonFlashPeer.prototype.ForceSetPropertyChange = function(propName,
                                                                    newValue,
                                                                    persist,
                                                                    propagate)
{
  var currVal = this.getComponent().getProperty(propName);
  //if the old and new values are the same, then set a different intermediate
  //value so that when we set the new value it will be treated as a change
  //that will be propagated to the middle tier
  if (currVal == newValue)
  {
    //use null as intermediate value
    var tmpNewVal = null;
    //if actual value is null, then use a different intermediate value
    if (currVal == null)
      tmpNewVal = "foo";
    //temporarily set the different intermediate value
    this.getComponent().setProperty(propName, tmpNewVal, persist, AdfUIComponent.PROPAGATE_NEVER);
  }
  //set the real property value
  this.getComponent().setProperty(propName, newValue, persist, propagate);
}

AdfDhtmlCommonFlashPeer.prototype.invokeLoadXml = function(movieObj, flashEventType, dataXml, paramKeys, paramVals) {
  var component = this.getComponent();
  //BUG FIX #11060989: update the size of the component in Flash
  //so that we can differentiate between a normal resize and a 
  //browser zoom
  AdfDhtmlCommonFlashPeer.updateSize(component, component.getPeer()._clientWidth, component.getPeer()._clientHeight);
  if (movieObj['loadXML']) {
    var clientId = component.getClientId();
    movieObj['loadXML'](clientId, flashEventType, dataXml, paramKeys, paramVals);
    return true;
  }  
  return false;
}

//function dvt_flash_hidePopup(popupId)
//{
//  AdfDhtmlCommonFlashPeer.hidePopup(popupId);
//}

/**
 * @export
 * Hide a popup triggered from Flash.
 * 
 * @param popupId the id of the popup to hide
 * @param clientId the id of the parent naming container of the popup
 */
AdfDhtmlCommonFlashPeer.hidePopup = function(popupId, clientId)
{
  var popup;
  if(clientId != null) {
    // If the client id is given, search from that component
    var component = AdfPage.PAGE.findComponent(clientId);
    if(component != null)
      popup = component.findComponent(popupId);
  }
  else
  {
    //BUG FIX #9530175: if we don't have a clientId and can't get the
    //component, just look for the popup on the page
    popup = AdfPage.PAGE.findComponent(popupId);
  }

  if (popup != null)
    popup.hide();
}

/**
 * Displays the fetch spinner for the component, indicating that a data fetch request
 * is pending from the server.
 */
AdfDhtmlCommonFlashPeer.prototype._displayFetchSpinner = function() {
  // Bug 18197141: Spinner won't go away on Firefox
  if (AdfAgent.AGENT.getPlatform() != AdfAgent.GECKO_PLATFORM) {
    // BUG #12805715: use timeout to sync time differences with Flash
    window.setTimeout(this.createCallback(this._showFetchSpinner), 500);
    
    // Set a flag indicating the spinner is pending
    this._spinnerPending = true;
  }
}

/**
 * Shows the fetch spinner for the component.
 */
AdfDhtmlCommonFlashPeer.prototype._showFetchSpinner = function() {
  // Continue only if spinner pending
  if(!this._spinnerPending)
    return;
  
  // Spinner will be shown, set flag to false
  this._spinnerPending = false;

  var component = this.getComponent();
  var clientId = component.getClientId();

  // Find the container div for the spinner, it is sent from the renderer
  var loadingDiv = document.getElementById(AdfRichUIPeer.CreateSubId(clientId, "loading"));
  if(!loadingDiv)
    return;

  var loadingBackground = document.getElementById(AdfRichUIPeer.CreateSubId(clientId, "loadingBackground"));
  loadingBackground.style.width = this._clientWidth + "px";
  loadingBackground.style.height = this._clientHeight + "px";

  var style = loadingDiv.style;
  if (style.display=="" || style.display=='none') {
    style.zIndex = 1000;
    style.display = 'block';
    style.visibility = 'visible';
    
    var loadingImg = document.getElementById(AdfRichUIPeer.CreateSubId(clientId, "loadingImg"));
    loadingImg.style.position = "absolute";
    loadingImg.style.top = Math.floor((this._clientHeight - loadingImg.clientHeight)/2) + 'px';
    loadingImg.style.left = Math.floor((this._clientWidth - loadingImg.clientWidth)/2) + 'px';
    
    var loadingBgImg = document.getElementById(AdfRichUIPeer.CreateSubId(clientId, "loadingBgImg"));
    loadingBgImg.style.position = "absolute";
    loadingBgImg.style.top = Math.floor((this._clientHeight - loadingBgImg.clientHeight)/2) + 'px';
    loadingBgImg.style.left = Math.floor((this._clientWidth - loadingBgImg.clientWidth)/2) + 'px';
  }
}

/**
 * Hides the fetch spinner for the component.
 */
AdfDhtmlCommonFlashPeer.prototype._hideFetchSpinner = function() {
  // If the spinner is about to be shown, prevent it from happening.
  this._spinnerPending = false;
   
  // Component will not be bound during InitDomElement, but we never need to hide in that case      
  var component = this.getComponent();
  if(component) {
    var clientId = component.getClientId();
  
    var loadingDiv = document.getElementById(AdfRichUIPeer.CreateSubId(clientId, "loading"));
    if(loadingDiv) {
      loadingDiv.style.display = 'none';
    }
  }
}

AdfDhtmlCommonFlashPeer.isTouchDevice = function()
{
    return (window && window.AdfSafariMobileAgent && AdfAgent.AGENT && AdfAgent.AGENT instanceof AdfSafariMobileAgent);
}

/**
 * Overrides AdfRichUIPeer for DVT toolkit component automation
 * @override
 */
AdfDhtmlCommonFlashPeer.prototype.GetSubIdDomElement = function(subId) {
  var auto = this.GetAutomation();
  return (auto == null ? null : auto.getDomElementForSubId(subId));
}

/**
 * Overrides AdfRichUIPeer for DVT toolkit component automation
 * @override
 */
AdfDhtmlCommonFlashPeer.prototype.GetBestMatchSubId = function(domElement) {
  var auto = this.GetAutomation();
  return (auto == null ? null : auto.getSubIdForDomElement(domElement));
}

/**
 * Returns the component implementation of a dvt.Automation object
 * @return {dvt.Automation} The component automation object
 * @protected
 */
AdfDhtmlCommonFlashPeer.prototype.GetAutomation = function() {
  return null;
}

/**
 * Returns whether the component is a child of a mashup
 * @return {boolean}
 * @protected
 */
AdfDhtmlCommonFlashPeer.prototype.IsInMashup = function() {
  return this.getComponent().getProperty(AdfDhtmlCommonFlashPeer.__IS_IN_MASHUP_KEY);
}
