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, preventScroll, isTabEvent) {
  AdfAssert.assertDomElement(element);
  AdfPopupScopingUtils.scope(element);
  AdfFocusUtils._focusElementImpl(element, preventScroll, isTabEvent);
};
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 <= -999) {
    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, preventScroll, isTabEvent) {
  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, preventScroll, isTabEvent);
  }
  return focusElement;
};
AdfFocusUtils.focusPreviousTabStop = function(element, contextElement, startOutside, preventScroll, isTabEvent) {
  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, preventScroll, isTabEvent);
  }
  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 && currentStyle.visibility == "hidden";
};
AdfFocusUtils._isDisplayNone = function(el, currentStyle) {
  if (!currentStyle) {
    currentStyle = AdfAgent.AGENT.getComputedStyle(el);
  }
  if (currentStyle && 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, preventScroll, isTabEvent) {
  try {
    var agent = AdfAgent.AGENT;
    var domWindow = agent.getDomWindow();
    if (element.setActive && agent.getVersion() < 11) {
      element.setActive();
    }
    if (preventScroll && preventScroll == true) {
      element.focus({preventScroll:true});
    } else {
      element.focus();
      var isTouch = domWindow.AdfSafariMobileAgent && agent instanceof AdfSafariMobileAgent;
      if (!isTouch && isTabEvent) {
        if (element.type == "text" && element.tagName == "INPUT") {
          element.setSelectionRange(0, element.value.length);
        }
      }
    }
  } catch (e) {
    AdfLogger.LOGGER.logErrorAsWarning(e, " when calling _focusElementImpl");
  }
};
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";
