var AdfFocusUtils = new Object;
AdfFocusUtils.containsFocus = function(element) {
  var focusElement = AdfPage.PAGE.getActiveDomElement();
  while(focusElement != null) {
    if(focusElement == element) {
      return true
    }
    focusElement = focusElement.parentNode
  }
  return false
};
AdfFocusUtils.focusElement = function(element) {
  AdfAssert.assertDomElement(element);
  AdfPopupScopingUtils.scope(element);
  AdfFocusUtils._focusElementImpl(element)
};
AdfFocusUtils.isFocusChangePending = function() {
  return AdfFocusUtils._delayFocusTimerId != null
};
AdfFocusUtils.focusFirstDocumentTabStop = function() {
  var body = AdfPage.PAGE.getDocument().body;
  var focusElement = AdfFocusUtils.getFirstTabStop(body, true);
  if(focusElement != null && focusElement.offsetTop < 0) {
    focusElement = AdfFocusUtils.getNextTabStop(focusElement)
  }
  if(focusElement != null) {
    AdfFocusUtils.focusElement(focusElement)
  }
  return focusElement
};
AdfFocusUtils.focusFirstTabStop = function(element, isExclusive) {
  AdfAssert.assertDomElement(element);
  var focusElement = AdfFocusUtils.getFirstTabStop(element, isExclusive);
  if(focusElement != null) {
    AdfFocusUtils.focusElement(focusElement)
  }
  return focusElement
};
AdfFocusUtils.focusLastTabStop = function(element, isExclusive) {
  AdfAssert.assertDomElement(element);
  var focusElement = AdfFocusUtils.getLastTabStop(element, isExclusive);
  if(focusElement != null) {
    AdfFocusUtils.focusElement(focusElement)
  }
  return focusElement
};
AdfFocusUtils.focusNextTabStop = function(element, contextElement, startOutside) {
  var focusElement = AdfFocusUtils.getNextTabStop(element, contextElement, startOutside);
  if(!focusElement) {
    if(!contextElement) {
      contextElement = element.ownerDocument.documentElement
    }
    focusElement = AdfFocusUtils.getFirstTabStop(contextElement, true)
  }
  if(focusElement) {
    AdfFocusUtils.focusElement(focusElement)
  }
  return focusElement
};
AdfFocusUtils.focusPreviousTabStop = function(element, contextElement, startOutside) {
  var focusElement = AdfFocusUtils.getPreviousTabStop(element, contextElement, startOutside);
  if(!focusElement) {
    if(!contextElement) {
      contextElement = element.ownerDocument.documentElement
    }
    focusElement = AdfFocusUtils.getLastTabStop(contextElement, true)
  }
  if(focusElement) {
    AdfFocusUtils.focusElement(focusElement)
  }
  return focusElement
};
AdfFocusUtils.getFirstTabStop = function(element, isExclusive) {
  return AdfDomUtils.getFirstElementMatch(element, isExclusive, AdfFocusUtils.isTabStop)
};
AdfFocusUtils.getLastTabStop = function(element, isExclusive) {
  return AdfDomUtils.getLastElementMatch(element, isExclusive, AdfFocusUtils.isTabStop)
};
AdfFocusUtils.getNextTabStop = function(element, contextElement, startOutside) {
  return AdfDomUtils.getNextElementMatch(element, contextElement, startOutside, AdfFocusUtils.isTabStop)
};
AdfFocusUtils.getPreviousTabStop = function(element, contextElement) {
  return AdfDomUtils.getPreviousElementMatch(element, contextElement, AdfFocusUtils.isTabStop)
};
AdfFocusUtils.isTabStop = function(el) {
  return el != null && el.tabIndex > -1 && AdfFocusUtils.isFocusable(el)
};
AdfFocusUtils.isFocusable = function(el, context) {
  return AdfFocusUtils.acceptFocusableNode(el, context) == AdfDomUtils.FILTER_ACCEPT
};
AdfFocusUtils.acceptFocusableNode = function(el, context) {
  if(el == null || el.nodeType != 1) {
    return AdfDomUtils.FILTER_REJECT
  }
  if(el.disabled) {
    return AdfDomUtils.FILTER_REJECT
  }
  var tabIdx = AdfDomUtils.getTabIndex(el);
  if(tabIdx < -1) {
    return AdfDomUtils.FILTER_REJECT
  }
  if(!AdfFocusUtils._isFocusableType(el)) {
    return AdfDomUtils.FILTER_SKIP
  }
  AdfFocusUtils._setupContext(context);
  if(AdfFocusUtils.isConnectedAndVisible(el, context)) {
    return AdfDomUtils.FILTER_ACCEPT
  }else {
    return AdfDomUtils.FILTER_REJECT
  }
};
AdfFocusUtils._setupContext = function(context) {
  if(!context) {
    return
  }
  if(!context.positiveValue) {
    var timestamp = (new Date).getTime();
    context.positiveValue = "y" + timestamp;
    context.negativeValue = "n" + timestamp
  }
};
AdfFocusUtils.calculateCachedValue = function(el, context, expando) {
  if(!context || !el || !expando) {
    return undefined
  }
  var agent = AdfAgent.AGENT;
  var cachedValue = agent.getAttribute(el, expando);
  if(cachedValue == context.positiveValue) {
    return true
  }else {
    if(cachedValue == context.negativeValue) {
      return false
    }
  }
  return undefined
};
AdfFocusUtils.setCacheValue = function(el, context, expando, value) {
  if(!context || !el || !expando || value == undefined) {
    return
  }
  var agent = AdfAgent.AGENT;
  var cacheValue = value ? context.positiveValue : context.negativeValue;
  agent.setExpandoProperty(el, expando, cacheValue)
};
AdfFocusUtils.isConnectedAndVisible = function(el, context) {
  AdfAssert.assertDomElement(el);
  var agent = AdfAgent.AGENT;
  var documentElement = agent.getDomDocument().documentElement;
  var cachedValue = AdfFocusUtils.calculateCachedValue(el, context, "_afrFoc");
  if(cachedValue != undefined) {
    return cachedValue
  }
  if(!AdfFocusUtils._isVisible(el)) {
    AdfFocusUtils.setCacheValue(el, context, "_afrFoc", false);
    return false
  }
  if(el == documentElement) {
    AdfFocusUtils.setCacheValue(el, context, "_afrFoc", true);
    return true
  }else {
    var parentValue;
    if(el.parentNode == null || el.parentNode.nodeType != 1) {
      parentValue = false
    }else {
      parentValue = AdfFocusUtils.isConnectedAndVisible(el.parentNode, context)
    }
    AdfFocusUtils.setCacheValue(el, context, "_afrFoc", parentValue);
    return parentValue
  }
};
AdfFocusUtils._isVisible = function(el, currentStyle) {
  if(!currentStyle) {
    currentStyle = AdfAgent.AGENT.getComputedStyle(el)
  }
  if(AdfFocusUtils._isDisplayNone(el, currentStyle)) {
    return false
  }
  return!AdfFocusUtils._isVisibilityHidden(el, currentStyle)
};
AdfFocusUtils._isVisibilityHidden = function(el, currentStyle) {
  if(!currentStyle) {
    currentStyle = AdfAgent.AGENT.getComputedStyle(el)
  }
  return currentStyle.visibility == "hidden"
};
AdfFocusUtils._isDisplayNone = function(el, currentStyle) {
  if(!currentStyle) {
    currentStyle = AdfAgent.AGENT.getComputedStyle(el)
  }
  if(currentStyle.display == "none") {
    if(el.style.display == "none" || !AdfPopupScopingUtils.isScopingContainer(el) || !AdfPopupScopingUtils.isOutOfScope(el)) {
      return true
    }
  }
  return false
};
AdfFocusUtils._isFocusableType = function(el) {
  AdfAssert.assertDomElement(el);
  var tabIdx = AdfDomUtils.getTabIndex(el);
  switch(el.nodeName.toLowerCase()) {
    case "a":
      if(!el.href && tabIdx == undefined) {
        return false
      }
    ;
    case "input":
      if(el.type == "hidden" && el.isContentEditable != true) {
        return false
      }
    ;
    case "area":
    ;
    case "body":
    ;
    case "button":
    ;
    case "frame":
    ;
    case "iframe":
    ;
    case "isindex":
    ;
    case "object":
    ;
    case "select":
    ;
    case "textarea":
      return true;
    default:
      if(tabIdx >= -1) {
        return true
      }
      return false
  }
};
AdfFocusUtils.focusElementDelayed = function(element, timeout) {
  AdfAssert.assertDomElement(element);
  AdfAssert.assertNumber(timeout);
  var page = AdfPage.PAGE;
  var timerId = AdfFocusUtils._delayFocusTimerId;
  if(timerId) {
    page.cancelTimer(timerId)
  }
  AdfFocusUtils._delayFocusTimerId = page.scheduleTimer(null, AdfFocusUtils._focusElementDelayedCallback, element, timeout)
};
AdfFocusUtils._focusElementDelayedCallback = function(element) {
  AdfFocusUtils._delayFocusTimerId = null;
  AdfFocusUtils._focusElementImpl(element)
};
AdfFocusUtils._focusElementImpl = function(element) {
  try {
    var agent = AdfAgent.AGENT;
    var domWindow = agent.getDomWindow();
    if(element.setActive && agent.getVersion() < 11) {
      element.setActive()
    }
    element.focus();
    var isTouch = domWindow.AdfSafariMobileAgent && agent instanceof AdfSafariMobileAgent;
    if(!isTouch) {
      if(element.type == "text" && element.tagName == "INPUT") {
        element.setSelectionRange(0, element.value.length)
      }
    }
  }catch(e) {
    AdfLogger.LOGGER.error(e)
  }
};
AdfFocusUtils.focusNextActiveDescendant = function(contextElement, currentActiveDescendant, wrap) {
  AdfAssert.assertDomElement(contextElement);
  AdfAssert.assertDomElement(currentActiveDescendant);
  var newActiveDescendant = AdfFocusUtils.getNextFocusableDescendant(contextElement, currentActiveDescendant);
  if(!newActiveDescendant) {
    if(wrap) {
      newActiveDescendant = AdfFocusUtils.getFirstFocusableDescendant(contextElement)
    }else {
      return newActiveDescendant
    }
  }
  if(newActiveDescendant && newActiveDescendant != currentActiveDescendant) {
    currentActiveDescendant.setAttribute("tabIndex", "-1");
    newActiveDescendant.setAttribute("tabIndex", "0");
    AdfFocusUtils.focusElement(newActiveDescendant)
  }
  return newActiveDescendant
};
AdfFocusUtils.focusPreviousActiveDescendant = function(contextElement, currentActiveDescendant, wrap) {
  AdfAssert.assertDomElement(contextElement);
  AdfAssert.assertDomElement(currentActiveDescendant);
  var newActiveDescendant = AdfFocusUtils.getPreviousFocusableDescendant(contextElement, currentActiveDescendant);
  if(!newActiveDescendant) {
    if(wrap) {
      newActiveDescendant = AdfFocusUtils.getLastFocusableDescendant(contextElement)
    }else {
      return newActiveDescendant
    }
  }
  if(newActiveDescendant && newActiveDescendant != currentActiveDescendant) {
    currentActiveDescendant.setAttribute("tabIndex", "-1");
    newActiveDescendant.setAttribute("tabIndex", "0");
    AdfFocusUtils.focusElement(newActiveDescendant)
  }
  return newActiveDescendant
};
AdfFocusUtils.setFirstActiveDescendant = function(contextElement, context) {
  AdfAssert.assertDomElement(contextElement);
  var newActiveDescendant = AdfFocusUtils.getFirstFocusableDescendant(contextElement, context);
  if(newActiveDescendant) {
    newActiveDescendant.setAttribute("tabIndex", "0")
  }
  return newActiveDescendant
};
AdfFocusUtils.setupCompositeWidget = function(contextElement) {
  AdfAssert.assertDomElement(contextElement);
  var context = {includeVisibilityHidden:true, includeDisplayNone:false, includeDisabled:true};
  var nextFocusableDescendant = AdfFocusUtils.getFirstFocusableDescendant(contextElement, context);
  while(nextFocusableDescendant) {
    nextFocusableDescendant.setAttribute("tabIndex", "-1");
    nextFocusableDescendant = AdfFocusUtils.getNextFocusableDescendant(contextElement, nextFocusableDescendant, context)
  }
  return AdfFocusUtils.setFirstActiveDescendant(contextElement)
};
AdfFocusUtils.getFirstFocusableDescendant = function(element, context) {
  AdfAssert.assertDomElement(element);
  return AdfDomUtils.getFirstFilteredElement(element, false, AdfFocusUtils.isFocusableDescendant, context)
};
AdfFocusUtils.getLastFocusableDescendant = function(element, context) {
  AdfAssert.assertDomElement(element);
  return AdfDomUtils.getLastFilteredElement(element, false, AdfFocusUtils.isFocusableDescendant, context)
};
AdfFocusUtils.getNextFocusableDescendant = function(contextElement, element, context) {
  AdfAssert.assertDomElement(contextElement);
  AdfAssert.assertDomElement(element);
  return AdfDomUtils.getNextFilteredElement(element, contextElement, true, AdfFocusUtils.isFocusableDescendant, context)
};
AdfFocusUtils.getPreviousFocusableDescendant = function(contextElement, element, context) {
  AdfAssert.assertDomElement(contextElement);
  AdfAssert.assertDomElement(element);
  return AdfDomUtils.getPreviousFilteredElement(element, contextElement, AdfFocusUtils.isFocusableDescendant, context)
};
AdfFocusUtils.getFirstUnmarkedTabstop = function(element) {
  AdfAssert.assertDomElement(element);
  return AdfDomUtils.getFirstFilteredElement(element, false, AdfFocusUtils.isUnmarkedTabstop)
};
AdfFocusUtils.isUnmarkedTabstop = function(element) {
  var logger = AdfLogger.LOGGER;
  if(element == null || element.nodeType != 1) {
    if(logger.isLoggable(AdfLogger.FINE)) {
      logger.fine("isUnmarkedTabstop: Null or not element node type")
    }
    return AdfDomUtils.FILTER_REJECT
  }
  var isDisplayNone = AdfFocusUtils._isDisplayNone(element);
  if(isDisplayNone) {
    if(logger.isLoggable(AdfLogger.FINE)) {
      logger.fine("isUnmarkedTabstop: Node display none: " + isDisplayNone + ", node tree: " + element.id + " " + element.nodeName)
    }
    return AdfDomUtils.FILTER_REJECT
  }
  var focusableAttribute = element.attributes[AdfFocusUtils.DESCENDANT_FOCUSABLE];
  if(focusableAttribute) {
    if(logger.isLoggable(AdfLogger.FINE)) {
      logger.fine("isUnmarkedTabstop: Node is a marked tabstop: " + element.id + " " + element.nodeName)
    }
    return AdfDomUtils.FILTER_REJECT
  }else {
    var isTabStop = AdfFocusUtils.isTabStop(element);
    if(isTabStop) {
      if(logger.isLoggable(AdfLogger.FINE)) {
        logger.fine("isUnmarkedTabstop: Found node that is an unmarked tabstop: " + element.id + " " + element.nodeName)
      }
      return AdfDomUtils.FILTER_ACCEPT
    }
    if(logger.isLoggable(AdfLogger.FINE)) {
      logger.fine("isUnmarkedTabstop: Node not a tabstop: " + element.id + " " + element.nodeName)
    }
    return AdfDomUtils.FILTER_SKIP
  }
};
AdfFocusUtils.isFocusableDescendant = function(element, context) {
  var logger = AdfLogger.LOGGER;
  context = context == null ? {includeVisibilityHidden:false, includeDisplayNone:false, includeDisabled:false} : context;
  if(element == null || element.nodeType != 1) {
    if(logger.isLoggable(AdfLogger.FINE)) {
      logger.fine("isFocusableDescendant? Skip and reject tree: Null or not element node type")
    }
    return AdfDomUtils.FILTER_REJECT
  }
  var elementStyle = AdfAgent.AGENT.getComputedStyle(element);
  var isVisibilityHidden = AdfFocusUtils._isVisibilityHidden(element, elementStyle);
  if(isVisibilityHidden && !Boolean(context.includeVisibilityHidden)) {
    if(logger.isLoggable(AdfLogger.FINE)) {
      logger.fine("isFocusableDescendant? Skip and reject tree, Node is visibility hidden, node: " + element.id + " " + element.nodeName)
    }
    return AdfDomUtils.FILTER_REJECT
  }
  var isDisplayNone = AdfFocusUtils._isDisplayNone(element, elementStyle);
  if(isDisplayNone && !Boolean(context.includeDisplayNone)) {
    if(logger.isLoggable(AdfLogger.FINE)) {
      logger.fine("isFocusableDescendant? Skip and reject tree, Node is display none, node: " + element.id + " " + element.nodeName)
    }
    return AdfDomUtils.FILTER_REJECT
  }
  var focusableAttribute = element.attributes[AdfFocusUtils.DESCENDANT_FOCUSABLE];
  if(!focusableAttribute) {
    if(logger.isLoggable(AdfLogger.FINE)) {
      logger.fine("isFocusableDescendant? Skip, node not focusable, node: " + element.id + " " + element.nodeName)
    }
    return AdfDomUtils.FILTER_SKIP
  }
  if(focusableAttribute.value == "true") {
    if(logger.isLoggable(AdfLogger.FINE)) {
      logger.fine("isFocusableDescendant? Accept, node focusable TRUE, node: " + element.id + " " + element.nodeName)
    }
    return AdfDomUtils.FILTER_ACCEPT
  }else {
    if(focusableAttribute.value == "false") {
      if(logger.isLoggable(AdfLogger.FINE)) {
        logger.fine("isFocusableDescendant? Node focusable FALSE, node: " + element.id + " " + element.nodeName)
      }
      return Boolean(context.includeDisabled) ? AdfDomUtils.FILTER_ACCEPT : AdfDomUtils.FILTER_REJECT
    }else {
      if(logger.isLoggable(AdfLogger.FINE)) {
        logger.fine("isFocusableDescendant? Skip, invalid value found: " + focusableAttribute.name + " " + focusableAttribute.value)
      }
      return AdfDomUtils.FILTER_SKIP
    }
  }
};
AdfFocusUtils.DESCENDANT_FOCUSABLE = "data-afr-fcs";
