(function(dvt) {
// Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
/**
 * @param {dvt.Context} context The rendering context.
 * @param {function} callback The function that should be called to dispatch component events.
 * @param {object} callbackObj The optional object instance on which the callback function is defined.
 * @constructor
 */
dvt.AdfDiagram = function(context, callback, callbackObj)
{
  this.Init(context, callback, callbackObj);
};

dvt.Obj.createSubclass(dvt.AdfDiagram, dvt.BaseDiagram, 'dvt.AdfDiagram');

dvt.AdfDiagram.INIT_ANIM_EXPLODE = 'initAnimExplode';
dvt.AdfDiagram.INIT_ANIM_ELASTIC_EXPLODE = 'initAnimElasticExplode';
dvt.AdfDiagram.INIT_ANIM_GROW_LINKS = 'initAnimGrowLinks';
dvt.AdfDiagram.INIT_ANIM_POP = 'initAnimPop';
dvt.AdfDiagram.INIT_ANIM_EXPLODE_GROW_LINKS = 'initAnimExplodeGrowLinks';
dvt.AdfDiagram.INIT_ANIM_ELASTIC_EXPLODE_GROW_LINKS = 'initAnimElasticExplodeGrowLinks';
dvt.AdfDiagram.INIT_ANIM_POP_GROW_LINKS = 'initAnimPopGrowLinks';

dvt.AdfDiagram.ISOLATE_TYPE_INTERSECT = 'isolateTypeIntersect';
dvt.AdfDiagram.ISOLATE_TYPE_UNION = 'isolateTypeUnion';
dvt.AdfDiagram.ISOLATE_TYPE_REPLACE = 'isolateTypeReplace';

dvt.AdfDiagram._ELEM_RESOURCES_CONTROLPANEL = 'controlPanelResources';
dvt.AdfDiagram._ELEM_RESOURCES_LEGEND = 'legendResources';
dvt.AdfDiagram._ELEM_RESOURCES_OVERVIEW = 'overviewResources';
dvt.AdfDiagram._ELEM_RESOURCES_PALETTE = 'paletteResources';
dvt.AdfDiagram._ELEM_RESOURCES_PANELDRAWER = 'panelDrawerResources';
dvt.AdfDiagram._ELEM_RESOURCES_SEARCHPANEL = 'searchPanelResources';
dvt.AdfDiagram._ELEM_RESOURCES_TRANSIENT_TOOLBAR = 'transientToolbarResources';
dvt.AdfDiagram._ELEM_RESOURCES_NODE = 'nodeResources';

dvt.AdfDiagram.OVERVIEW_MAX_WIDTH = 145;
dvt.AdfDiagram.OVERVIEW_MAX_HEIGHT = 118;

/**
 * Overview style for non-disclosed node
 * @const
 */
dvt.AdfDiagram.OVERVIEW_STYLE = 'overviewStyle';
/**
 * Overview style for disclosed node
 * @const
 */
dvt.AdfDiagram.OVERVIEW_CONTAINER_STYLE = 'overviewContainerStyle';


/**
 * Enabled button state
 * @const
 */
dvt.AdfDiagram.BUTTON_STATE_ENA = 0;


/**
 * Hover button state
 * @const
 */
dvt.AdfDiagram.BUTTON_STATE_OVR = 1;


/**
 * Pressed down button state
 * @const
 */
dvt.AdfDiagram.BUTTON_STATE_DWN = 2;


/**
 * Disabled button state
 * @const
 */
dvt.AdfDiagram.BUTTON_STATE_DIS = 3;


/**
 * @private
 */
dvt.AdfDiagram._HIGHLIGHT_ANIM_DELAY = .15;


/**
 * @private
 */
dvt.AdfDiagram.UNGROUPED_ID = '__UNGROUPED__';


/**
 * @private
 */
dvt.AdfDiagram._SEARCH_PANEL_POS_PADDING = 5;

dvt.AdfDiagram.LAYOUT_KEY = 'layoutInfo';
dvt.AdfDiagram.ROW_DISCLOSURE_KEY = 'rowDisclosureInfo';
dvt.AdfDiagram.HAS_ROW_DISCLOSURE_LISTENER_KEY = 'hasRowDisclosureListener';
dvt.AdfDiagram.HAS_DRILL_LISTENER_KEY = 'hasDrillListener';
dvt.AdfDiagram.ISOLATED_NODES_KEY = 'isolatedNodesInfo';
dvt.AdfDiagram.GROUP_BY_KEY = 'groupByInfo';
dvt.AdfDiagram.GROUP_BY_UNGROUPED_KEY = 'groupByUngroupedInfo';
dvt.AdfDiagram.GROUP_BY_HIDDEN_KEY = 'groupByHiddenInfo';
dvt.AdfDiagram.ROOT_ROW_KEY = 'rootRowKeyInfo';
dvt.AdfDiagram.PREVIEW_ROW_KEY = 'previewRowKey';
dvt.AdfDiagram.PREVIEW_PAN_ZOOM_KEY = 'previewPanZoom';

dvt.AdfDiagram._NODE_DETAIL_DELAY = 800; //in ms

/*-------------------------------------------------------------------------*/
/*   PanelCard                                                             */
/*-------------------------------------------------------------------------*/
dvt.AdfDiagram.PANELCARD_FETCH_EVENT = 'hv_pcfet';

dvt.AdfDiagram.ROWKEY_DELIMITER = '$afr$';
dvt.AdfDiagram.SYNC_PANEL_CARDS_KEY = 'syncCards';


dvt.AdfDiagram._BUTTON_SIZE = 44;
dvt.AdfDiagram._WIDE_BUTTON_SIZE = 90;


dvt.AdfDiagram._RERENDER_WAIT_TIME_DESKTOP = 250;
dvt.AdfDiagram._RERENDER_WAIT_TIME_MOBILE = 350;

/**
 * Returns a new instance of dvt.AdfDiagram.
 * @param {dvt.Context} context The rendering context.
 * @param {function} callback The function that should be called to dispatch component events.
 * @param {object} callbackObj The optional object instance on which the callback function is defined.
 * @return {dvt.AdfDiagram}
 */
dvt.AdfDiagram.newInstance = function(context, callback, callbackObj) {
  return new dvt.AdfDiagram(context, callback, callbackObj);
};

/**
 * Initialization method called by the constructor
 *
 * @param {dvt.Context} context The rendering context.
 * @param {function} callback The function that should be called to dispatch component events.
 * @param {object} callbackObj The optional object instance on which the callback function is defined.
 */
dvt.AdfDiagram.prototype.Init = function(context, callback, callbackObj)
{
  dvt.AdfDiagram.superclass.Init.call(this, context, callback, callbackObj);

  if (dvt.Agent.isTouchDevice())
    this.RerenderWaitTime = dvt.AdfDiagram._RERENDER_WAIT_TIME_MOBILE;
  else
    this.RerenderWaitTime = dvt.AdfDiagram._RERENDER_WAIT_TIME_DESKTOP;

  // Create the event handler and add event listeners
  this.EventManager = new dvt.AdfDiagramEventManager(context, this._handleEvent, this);
  this.EventManager.addListeners(this);
  this.EventManager.addRolloverType(dvt.AdfDiagramNode);
  this.EventManager.addRolloverType(dvt.AdfDiagramLink);

  this._dragSource = this.CreateDragSource(context);
  this._dropTarget = new DvtAdfDiagramDropTarget(this);
  this.EventManager.setDragSource(this._dragSource);
  this.EventManager.setKeyboardHandler(new DvtAdfDiagramKeyboardHandler(this, this.EventManager));

  this._nodes = {};
  this._nodesByRowIndex = {};
  this._arNodeIds = [];
  this._links = {};
  this._linksByRowIndex = {};
  this._arLinkIds = [];

  this._nodeDefs = {};
  this._linkDefs = {};

  this._layout = null;
  this._layoutCallback = null;
  this._layoutAttrs = null;

  this._rootNodes = [];

  this._nodeHighlightBehavior = null;
  this._linkHighlightBehavior = null;
  this._bNodeDetail = false;
  this._bPreventNodeDetailWindow = false;
  this._dataChangeAnim;
  this._bAnimTransition = false;

  this._arAttrGrps = null;
  this._compoundAttrGrpsMap = null;
  this._attrGrpsMap = null;
  this._arHiddenAttrGrps = null;

  this._isolatedNodes = null;
  this._isolatedNodeMap = null;

  this._readOnly = true;
  this._legend = null;
  this._palette = null;
  this._overviewWindow = null;
  this._overviewContent = null;

  this._oldLegendAlpha = 1;
  this._oldOverviewAlpha = 1;

  this._rolloverBehavior = null;

  this._bCrossedZoomThreshold = false;

  //timers for node highlighting
  this._highlightOverTimer = null;

  //dvt.AdfDiagramTransitionUtils.processStack() will set these
  this._stackNodeMap;
  this._nodeStackMap;

  this._restoreButton;

  this._arCrossContainerLinkIds = null;
  this._drilledNodePath = null;
  this._drillBreadcrumbs = null;
  this._oldDrillBreadcrumbs = null;

  this._fakeNodeDefCount = 0;
  this._nodeCreateEnabled = true;
  this._linkCreateEnabled = true;

  this._ignoreRollover = false;

  this._panningEnabled = true;
  this._zoomingEnabled = true;
  this._showRestore = null;

  this._centerNode = null;

  //drop target styles
  this._dropFeedbackFillColor = null;
  this._dropFeedbackBorderColor = null;
  this._dropFeedbackFillAlpha = null;

  this._panelDrawer = null;
  this._searchPanel = null;
  this._transientToolbarDataId = null;
  //always need to listen for click and mousedown for node transient toolbar
  this.addEvtListener(dvt.MouseEvent.CLICK, this.HandleClick, false, this);
  this.addEvtListener(dvt.MouseEvent.MOUSEDOWN, this.HandleMouseDown, false, this);
  this._bHandleSelectionClick = false;

  //Bug 15838158: id of node whose stack we're currently previewing
  this._stackPreviewNodeId = null;

  //flag indicating if the hover details were launched from the keyboard
  this._bKeyboardHoverDetails = false;

  //skinning
  this._ovContentStyle = null;
  this._nodeStyles = {};
  this._linkStyles = {};
  this._eventType = null;

  this._buttonBorderRadius = null;

  //panelCard
  this._cardCSSStyle = null;
  this._cardHeaderStyle = {};
  this._cardNavButtonStyle = null;
  this._cardNavButtonHoverStyle = null;
  this._cardNavButtonActiveStyle = null;
  this._cardNavButtonDisabledStyle = null;
  this._cardTrainButtonVisible = true;
  this._cardTrainButtonStyle = null;
  this._cardTrainButtonActiveStyle = null;
  this._cardNextButtonIcon = null;
  this._cardNextButtonHoverIcon = null;
  this._cardNextButtonActiveIcon = null;
  this._cardNextButtonDisabledIcon = null;
  this._cardPrevButtonIcon = null;
  this._cardPrevButtonHoverIcon = null;
  this._cardPrevButtonActiveIcon = null;
  this._cardPrevButtonDisabledIcon = null;

  this._cardIndexes = [];
  this._nodeAndLoadedCardsMap = {};

  this._visibleNodesByGroupDepth = null;

  this._bSyncCardOff = false;
  this.setDisplayedControls(dvt.ControlPanel.CONTROLS_ALL & ~dvt.ControlPanel.CONTROLS_CENTER_BUTTON);

  this._showPopupBehaviors = null;
  this._interactionDisableCounter = 0;
  //Map to store diagram node states for automation
  this._automationStatesMap = null;
};

/**
 * Sets diagram interaction mode - interaction should be disabled when diagram is not fully rendered or during component animation.
 * The component interaction is disabled on the first setInteractionEnabled(false) call and it is re-enabled
 * when it is paired with setInteractionEnabled(true) call.
 * The disable/enable calls can be called from nested methods, but it is important to match each disable call
 * with the corresponding enable call.
 * @param {boolean} bEnabled True to enable interaction
 */
dvt.AdfDiagram.prototype.setInteractionEnabled = function(bEnabled) {
  var newCounter = bEnabled ? this._interactionDisableCounter - 1 : this._interactionDisableCounter + 1;
  if (this._interactionDisableCounter == 0 && newCounter == 1) { //increase counter value from 0 to 1
    this.EventManager.removeListeners(this);
    this.getPanZoomCanvas().setInteractionEnabled(false);
    if (this._palette)
      this._palette.setInteractionEnabled(false);
  }
  else if (this._interactionDisableCounter == 1 && newCounter == 0) { //decrease counter value from 1 to 0
    this.EventManager.addListeners(this);
    this.getPanZoomCanvas().setInteractionEnabled(true);
    if (this._palette)
      this._palette.setInteractionEnabled(true);
  }
  this._interactionDisableCounter = newCounter;
};

/**
 * Resets diagram interaction mode, occurs when a full diagram render occurs
 */
dvt.AdfDiagram.prototype.resetInteractionEnabled = function() {
  this._interactionDisableCounter = 0;
  this.clearAutomationState();
};

/**
 * Sets the diagram Animation
 * @param {dvt.Playable} animation
 */
dvt.AdfDiagram.prototype.setAnimation = function(animation) {
  this.Animation = animation;
};

dvt.AdfDiagram.prototype.CreateDragSource = function(context) {
  return new DvtAdfDiagramDragSource(context, this);
};

dvt.AdfDiagram.prototype.GetInitAnimHelper = function() {
  return new dvt.AdfDiagramInitAnim(this.getCtx(), this);
};

dvt.AdfDiagram.prototype.setNodeHighlightBehavior = function(highlightBehavior)
{
  this._nodeHighlightBehavior = highlightBehavior;
};

dvt.AdfDiagram.prototype.getNodeHighlightBehavior = function()
{
  return this._nodeHighlightBehavior;
};

dvt.AdfDiagram.prototype.setLinkHighlightBehavior = function(highlightBehavior)
{
  this._linkHighlightBehavior = highlightBehavior;
};

dvt.AdfDiagram.prototype.getLinkHighlightBehavior = function()
{
  return this._linkHighlightBehavior;
};

dvt.AdfDiagram.prototype.setNodeDetail = function(bNodeDetail)
{
  this._bNodeDetail = bNodeDetail;
};

dvt.AdfDiagram.prototype.isNodeDetail = function()
{
  return this._bNodeDetail;
};

/**
 * @override
 */
dvt.AdfDiagram.prototype.setSelectionMode = function(selectionMode) {
  if (selectionMode &&
      (selectionMode == dvt.SelectionHandler.TYPE_SINGLE || selectionMode == dvt.SelectionHandler.TYPE_MULTIPLE)) {
    this._selectionHandler = new dvt.SelectionHandler(selectionMode);
    // Disable "sticky" highlighting
    this._bHandleSelectionClick = false;
  }
  else {
    this._selectionHandler = null;
    // Enable "sticky" highlighting
    this._bHandleSelectionClick = true;
  }
  // Event Handler delegates to other handlers
  this.EventManager.setSelectionHandler(this._selectionHandler);
};

/**
 * Sets the behavior when the mouse rolls over one object in an attributeGroup
 * @param {string} rolloverBehavior the values are "none" (default) or "dim"
 */
dvt.AdfDiagram.prototype.setRolloverBehavior = function(rolloverBehavior) {
  this._rolloverBehavior = rolloverBehavior;
};

/**
 * Gets the behavior when the mouse rolls over one object in an attributeGroup
 * @return {string} the values are "none" (default) or "dim"
 */
dvt.AdfDiagram.prototype.getRolloverBehavior = function() {
  return this._rolloverBehavior;
};


/**
 * Set Legend JSON object
 * @param {object} jsonData  Legend JSON object
 */
dvt.AdfDiagram.prototype.SetLegendJson = function(jsonData) {
  this._legendJson = jsonData;
};

/**
 * Set Palette JSON object
 * @param {object} jsonData  palette json boject
 */
dvt.AdfDiagram.prototype.SetPaletteJson = function(jsonData) {
  this._paletteJson = jsonData;
};

/**
 * Callback method that handles legend events
 * @param {dvt.ResizeEvent|object} event
 * @param {object} source The component that is the source of the event, if available
 */
dvt.AdfDiagram.prototype.processLegendEvent = function(event, source) {
  var type = event['type'];
  if (type == 'categoryHide' || type == 'categoryShow') {
    this._processHideShowEvent(event);
  }
  else if (type == 'categoryHighlight') {
    this._processRolloverEvent(event);
  }
  else if (type == dvt.ResizeEvent.RESIZE_EVENT && this._legend) {
    this._legend.FireListener(event);
  }
};

/**
 * Processes legend hide/show event
 * @param {object} event hide/show event
 * @private
 */
dvt.AdfDiagram.prototype._processHideShowEvent = function(event) {
  var type = event['type'];
  var category = event['category'];
  var hiddenAttrGroups = this.getHiddenAttributeGroups();
  if (!hiddenAttrGroups) {
    hiddenAttrGroups = [];
  }
  hiddenAttrGroups = hiddenAttrGroups.slice(0); // clone so we don't modify the original

  var categoryIndex = dvt.ArrayUtils.getIndex(hiddenAttrGroups, category);
  if (type == 'categoryHide' && categoryIndex == -1) {
    if ('dim' == this.getRolloverBehavior()) {
      // Undo fading
      this._fireRolloverEvent(dvt.EventFactory.newCategoryHighlightEvent(category, false));
    }
    hiddenAttrGroups.push(category);
  }
  if (type == 'categoryShow' && categoryIndex != -1) {
    hiddenAttrGroups.splice(categoryIndex, 1);
  }
  if (hiddenAttrGroups.length == 0) {
    hiddenAttrGroups = null;
  }
  this.setHiddenAttributeGroups(hiddenAttrGroups);
};

/**
 * Processes legend rollover event
 * @param {object} event rollover event
 * @private
 */
dvt.AdfDiagram.prototype._processRolloverEvent = function(event) {
  if (this._ignoreRollover) {
    return;
  }

  var category = event['categories'];
  var hiddenAttrGroups = this.getHiddenAttributeGroups();
  if (!hiddenAttrGroups) {
    hiddenAttrGroups = [];
  }
  var index = dvt.ArrayUtils.getIndex(hiddenAttrGroups, category);
  if (index != -1) {
    return; // ignore rollover on hidden attribute groups
  }
  this._fireRolloverEvent(event);
};

/**
 * Finds objects that belong to the given category and processes the event
 * @param {object} rolloverEvent rollover event
 * @private
 */
dvt.AdfDiagram.prototype._fireRolloverEvent = function(rolloverEvent) {
  // Build object list out of nodes, links
  var objects = new Array();
  for (var nodeId in this._nodes)
  {
    var node = this.getNodeById(nodeId);
    objects.push(node);
  }

  for (var linkId in this._links)
  {
    var link = this.getLinkById(linkId);
    objects.push(link);
  }

  dvt.CategoryRolloverHandler.highlight(rolloverEvent['categories'], objects, false, 0.10);
};

dvt.AdfDiagram.prototype.StopNodeDetailTimer = function()
{
  if (this._nodeDetailTimer)
  {
    this._nodeDetailTimer.stop();
    this._nodeDetailTimer = null;
  }
};

dvt.AdfDiagram.prototype.StartNodeDetailTimer = function()
{
  this._nodeDetailTimer = new dvt.Timer(this.getCtx(), dvt.AdfDiagram._NODE_DETAIL_DELAY, this.HandleNodeDetailTimer, this, 1);
  this._nodeDetailTimer.start();
};

dvt.AdfDiagram.prototype.HandleNodeDetailTimer = function() {
  this.StopNodeDetailTimer();

  //BUG FIX #8264052: don't show hover window when mouse is
  //over interactive component, like a button, or component
  //with a tooltip, like truncated text
  var currMouseTarget = null;
  //only have currMouseTarget if node details were not launched by a
  //keyboard shortcut
  if (!this._bKeyboardHoverDetails) {
    currMouseTarget = this.getEventManager().getCurrentMouseTarget();
  }
  var bPrevent = false;
  while (currMouseTarget) {
    if (currMouseTarget instanceof dvt.Button ||
        currMouseTarget instanceof dvt.Train ||
        ((currMouseTarget instanceof dvt.OutputText || currMouseTarget instanceof dvt.BackgroundOutputText) && currMouseTarget.isTruncated()) ||
        (currMouseTarget instanceof dvt.Text && currMouseTarget.isTruncated())) {
      bPrevent = true;
      break;
    }
    else {
      //don't show the hover window if the AF component has a showPopupBehavior
      var afComp = dvt.AfComponentFactory.getAfComponent(currMouseTarget);
      if (afComp && afComp.getShowPopupBehaviors()) {
        bPrevent = true;
        break;
      }
    }
    currMouseTarget = currMouseTarget.getParent();
  }
  if (bPrevent) {
    this.HideNodeHoverDetails();
  }

  if (this._detailNode) {
    var detailNodeContents = this.CreateNodeDetailContent();
    if (detailNodeContents) {
      //Bug 14374448 - DIAGRAMMER: <AF:OUTPUTFORMATTED> SIZES ITS CONTAINING LAYOUT INCONSISTENTLY
      // var detailDims = detailNodeContents.getDimensions();
      var detailDims = dvt.AfComponentFactory.getBounds(detailNodeContents);
      var padding = new dvt.Dimension(.1 * detailDims.w, .1 * detailDims.h);

      this._detailNodeContents = this.CreateNodeDetailWindow(detailNodeContents, detailDims, padding);

      this.PositionNodeDetailWindow(this._detailNodeContents, detailDims, padding);
    }
  }
};

dvt.AdfDiagram.prototype.CreateNodeDetailContent = function() {
  var afContext = this.createAfContext();

  var detailNodeContents = this._detailNode.createDetailContents(afContext);
  if (detailNodeContents) {
    this._detailNode.renderDetailContents(afContext);
  }
  return detailNodeContents;
};

dvt.AdfDiagram.prototype.CreateNodeDetailWindow = function(detailNodeContents, detailDims, padding) {
  //Bug 14374448 - DIAGRAMMER: <AF:OUTPUTFORMATTED> SIZES ITS CONTAINING LAYOUT INCONSISTENTLY
  // var nodeDims = this._detailNode.getDimensions();
  //var nodeDims = dvt.AfComponentFactory.getBounds(this._detailNode);
  var ndWindow = new dvt.Rect(this.getCtx(), 0, 0, detailDims.w + 2 * padding.w, detailDims.h + 2 * padding.h);
  var fill = new dvt.SolidFill('#cccccc', .8);
  //var stroke = new dvt.SolidStroke("#000000", .7, 1);
  ndWindow.setFill(fill);
  //ndWindow.setStroke(stroke);

  detailNodeContents.getParent().addChild(ndWindow);
  ndWindow.addChild(detailNodeContents);
  detailNodeContents.setTranslate(padding.w, padding.h);
  return ndWindow;
};

dvt.AdfDiagram.prototype.PositionNodeDetailWindow = function(ndWindow, detailDims, padding) {
  var currZoom = this.getPanZoomCanvas().getZoom();
  var scale = (1 / currZoom);
  ndWindow.setTranslate(this._detailNode.getTranslateX() - scale * (detailDims.w + 2 * padding.w), this._detailNode.getTranslateY() - scale * (detailDims.h + 2 * padding.h));
  ndWindow.setScale(scale, scale);
};


/**
 * Handles an event from the EventManager
 * @param {dvt.BaseComponentEvent} event The event to be dispatched.
 */
dvt.AdfDiagram.prototype._handleEvent = function(event) {
  var type = event['type'];
  if (type == dvt.ClientBehaviorEvent.TYPE) {
    this.HandleClientBehaviorEvent(event);
    return;
  }
  this.dispatchEvent(event);
};

/**
 * Handles a dvt.ClientBehaviorEvent
 * @param {dvt.ClientBehaviorEvent} event the dvt.ClientBehaviorEvent
 * @protected
 */
dvt.AdfDiagram.prototype.HandleClientBehaviorEvent = function(event) {
  var suppressAnimation = event.getParamValue('suppressAnimation');
  var clientBehavior = event.getClientBehavior();
  var behaviorType = clientBehavior.getType();
  if (dvt.ClientBehavior.TYPE_RESTORE == behaviorType) {
    this.HandleRestore(null, null, suppressAnimation);
  }
  else {
    var clientRowKey = event.getParamValue('clientRowKey');
    if (clientRowKey && clientRowKey.length > 1) {
      if (clientRowKey.charAt(0) == 'N') {
        var node = this.getNodeById(clientRowKey);
        if (node) {
          if (dvt.ClientBehavior.TYPE_ISOLATE_LEVELS == behaviorType) {
            var levels = parseInt(clientBehavior.getProperty('levels'));
            node.HandleIsolate(null, null, suppressAnimation, levels);
          }
          else if (node.isContainer()) {
            if (!node.getData().isChildrenFetched()) {
              // We're about to queue an event back to the server, so cancel any pending animation (in the case of multiple client behaviors)
              // all behaviors will be animated after the server response
              var clientBehaviorHandler = this.getEventManager().ClientBehaviorHandler;
              if (clientBehaviorHandler.suspendAnimation) {
                clientBehaviorHandler.suspendAnimation();
              }
            }
            if (dvt.ClientBehavior.TYPE_ROW_DISCLOSURE == behaviorType) {
              if (node.isDisclosed()) {
                node.HandleContainerClose(null, null, suppressAnimation);
              }
              else {
                node.HandleContainerOpen(null, null, suppressAnimation);
              }
            }
            else if (dvt.ClientBehavior.TYPE_DRILL == behaviorType) {
              node.HandleDrill(null, null, suppressAnimation);
            }
          }
        }
      }
    }
  }
};

dvt.AdfDiagram.prototype.HandleMouseDown = function(event)
{
  //  if (this._bHandleSelectionClick) {
  this._bDragged = false;
  this.addEvtListener(dvt.MouseEvent.MOUSEMOVE, this.HandleMouseMove, false, this);
  this.addEvtListener(dvt.MouseEvent.MOUSEUP, this.HandleMouseUp, false, this);
  //  }
};

dvt.AdfDiagram.prototype.HandleMouseMove = function(event)
{
  this._bDragged = true;
};

dvt.AdfDiagram.prototype.HandleMouseUp = function(event)
{
  this._bDragged = false;
  this.removeEvtListener(dvt.MouseEvent.MOUSEMOVE, this.HandleMouseMove, false, this);
  this.removeEvtListener(dvt.MouseEvent.MOUSEUP, this.HandleMouseUp, false, this);
};

dvt.AdfDiagram.prototype.HandleClick = function(event) {
  //  if (this._bHandleSelectionClick) {
  if (this._bDragged) {
    return;
  }
  if (this._bIgnoreNextClick) {
    this._bIgnoreNextClick = false;
    return;
  }
  /*
    if (this._bHighlighting && this._selectedObj) {
      if (this._selectedObj instanceof dvt.AdfDiagramNode) {
        this.ShowHideNodeHoverEffect(this._selectedObj, false);
      }
      else if (this._selectedObj instanceof dvt.AdfDiagramLink) {
        this.ShowHideLinkHoverEffect(this._selectedObj, false);
      }
      this._selectedObj = null;
    }
    */
  //  }

  //hide the flyouts if necessary
  this.HideNodeTransientToolbar();
};

dvt.AdfDiagram.prototype.HandleNodeClick = function(node, event) {
  if (this._bDragged) {
    return;
  }

  //show the flyouts if necessary
  if (this._transientToolbarDataId != node.getId()) {
    this.ShowNodeTransientToolbar(node, event);
  } else {
    this.HideNodeTransientToolbar(false);
  }

  this._bIgnoreNextClick = true;

  if (this.getSelectionMode()) {
    return;
  }
  /*
  if (this._bHighlighting && !this._selectedObj) {
    this._selectedObj = node;
  }
  */
};

dvt.AdfDiagram.prototype.HandleNodeDblClick = function(node) {
  // Fire a set anchor event
  var event = dvt.EventFactory.newActionEvent('setAnchor', node.getData().getTemplateId());
  event[dvt.BaseComponentEvent.CLIENT_ROW_KEY] = node.getData().getId();
  this.EventManager.FireEvent(event, this);
};

dvt.AdfDiagram.prototype.HandleLinkClick = function(link) {
  //hide the flyouts if necessary
  this.HideNodeTransientToolbar();

  this._bIgnoreNextClick = true;

  if (this.getSelectionMode()) {
    return;
  }
  /*
  if (this._bHighlighting && !this._selectedObj) {
    this._selectedObj = link;
  }
  */
};
/**
 * Show node hover effect
 *
 * @param {dvt.AdfDiagramNode} node the node that has hover effect turned on
 * @param {boolean} immediate true if hover effect should occur immediately
 */
dvt.AdfDiagram.prototype.ShowNodeHoverEffect = function(node, immediate)
{
  if (!this._selectedObj)
  {
    this.ShowHideNodeHoverEffect(node, true, immediate);
  }
};
/**
 * Hide node hover effect
 *
 * @param {dvt.AdfDiagramNode} node the node that has hover effect removed
 */
dvt.AdfDiagram.prototype.HideNodeHoverEffect = function(node)
{
  if (!this._selectedObj)
  {
    this.ShowHideNodeHoverEffect(node, false);
  }
};

dvt.AdfDiagram.prototype.ShowNodeHoverDetails = function(node, bImmediate)
{
  //if there's an existing hover window shown, hide it first
  if (this._detailNode) {
    this.HideNodeHoverDetails();
  }

  if (this.isNodeDetail() && this.getPanZoomCanvas().getZoom() <= .75)
  {
    this._detailNode = node;
    this.StopNodeDetailTimer();
    if (bImmediate) {
      this.HandleNodeDetailTimer();
    }
    else {
      this.StartNodeDetailTimer();
    }
  }
};

dvt.AdfDiagram.prototype.HideNodeHoverDetails = function()
{
  this.StopNodeDetailTimer();
  this._detailNode = null;
  if (this._detailNodeContents)
  {
    this._detailNodeContents.getParent().removeChild(this._detailNodeContents);
    this._detailNodeContents = null;
  }
};
/**
 * Show link hover effect
 *
 * @param {dvt.AdfDiagramLink} link the link that has hover effect turned on
 * @param {boolean} immediate true if hover effect should occur immediately
 */
dvt.AdfDiagram.prototype.ShowLinkHoverEffect = function(link, immediate)
{
  if (!this._selectedObj)
  {
    this.ShowHideLinkHoverEffect(link, true, immediate);
  }
};
/**
 * Hide link hover effect
 *
 * @param {dvt.AdfDiagramLink} link the link that has hover effect removed
 */
dvt.AdfDiagram.prototype.HideLinkHoverEffect = function(link)
{
  if (!this._selectedObj)
  {
    this.ShowHideLinkHoverEffect(link, false);
  }
};
/**
 * Show or hide node hover effect
 *
 * @param {dvt.AdfDiagramNode} node the node that has hover effect turned on or off
 * @param {boolean} bOver true if node is hovered over
 * @param {boolean} immediate true if hover effect should take place immediately
 */
dvt.AdfDiagram.prototype.ShowHideNodeHoverEffect = function(node, bOver, immediate)
{
  if (this._ignoreRollover) {
    return;
  }

  if (bOver)
  {
    //if there's not already a mouseover timer running, create a
    //new one and start it
    if (!this._highlightOverTimer)
    {
      if (!immediate) {
        var alphaFunc = function() {
          this.ShowHideNodeHoverEffectHelper(node, bOver);
        };
        this._highlightOverTimer = new dvt.Timer(this.getCtx(), 1000 * dvt.AdfDiagram._HIGHLIGHT_ANIM_DELAY, alphaFunc, this, 1);
        this._highlightOverTimer.start();
      }
      else {
        this.ShowHideNodeHoverEffectHelper(node, bOver);
      }
    }
  }
  else
  {
    //if there's a mouseover timer running, stop it
    if (this._highlightOverTimer)
    {
      this._highlightOverTimer.stop();
      this._highlightOverTimer = null;
    }
    this.ShowHideNodeHoverEffectHelper(node, bOver);
  }
};

/**
 * Show/Hide Node Hover effect helper
 * @param {dvt.AdfDiagramNode} node diagram node
 * @param {boolean} bOver flag indicates show/hide hover effect
 * @protected
 */
dvt.AdfDiagram.prototype.ShowHideNodeHoverEffectHelper = function(node, bOver)
{
  if (this.getSelectionMode()) {
    //set flag on node, because node doesn't do it itself anymore
    node._hover = bOver;
  }
  var bHighlighting = this._nodeHighlightBehavior != null && (this._nodeHighlightBehavior.indexOf('Node') != -1);

  if (bHighlighting) {
    var highlightedObjects = {};
    // Add the node and its descendants, along with any links contained entirely within the node
    var nodeAndDescendants = this._addHighlightedObjects(node.getData().getId(), highlightedObjects);

    var incoming = this._nodeHighlightBehavior.indexOf('In') != -1;
    var outgoing = this._nodeHighlightBehavior.indexOf('Out') != -1;
    var endpoints = this._nodeHighlightBehavior.indexOf('End') != -1;
    if (incoming || outgoing) {
      for (var nodeId in nodeAndDescendants) {
        var curNode = this.getNodeById(nodeId);
        var i;
        var id;
        var link;
        if (incoming) {
          var inLinkIds = curNode.getInLinkIds();
          if (inLinkIds)
          {
            for (i = 0; i < inLinkIds.length; i++)
            {
              id = inLinkIds[i];
              link = this.getLinkById(id);
              highlightedObjects[id] = link;
              if (endpoints) {
                var startId = link.getData().getStartId();
                if (link.isPromoted()) {
                  startId = this._containerLinkPromotionMap[id][0];
                }
                if (link)
                  this._addHighlightedObjects(startId, highlightedObjects);
              }
            }
          }
        }
        if (outgoing) {
          var outLinkIds = curNode.getOutLinkIds();
          if (outLinkIds)
          {
            for (i = 0; i < outLinkIds.length; i++)
            {
              id = outLinkIds[i];
              link = this.getLinkById(id);
              highlightedObjects[id] = link;
              if (endpoints) {
                var endId = link.getData().getEndId();
                if (link.isPromoted()) {
                  endId = this._containerLinkPromotionMap[id][1];
                }
                this._addHighlightedObjects(endId, highlightedObjects);
              }
            }
          }
        }
      }
    }
    this._updateAlphas(bOver, highlightedObjects);
  }
};

/**
 * @private
 * Add the node/link to the listed of highlighted objects
 * @param {string} nodeId node id
 * @param {object} allHighlightedObjects highlighted objects
 * @return {object}  map of decendants
 */
dvt.AdfDiagram.prototype._addHighlightedObjects = function(nodeId, allHighlightedObjects) {
  var descendants = {};
  var descendantMap = this._getDescendants(nodeId, true); // for interactive highlighting, doesn't matter whether node's children have been fetched or not
  for (var id in descendantMap) {
    descendants[id] = this.getNodeById(id);
    allHighlightedObjects[id] = descendants[id];
  }
  for (var linkId in this._links) {
    var link = this.getLinkById(linkId);
    var startId = link.getData().getStartId();
    var endId = link.getData().getEndId();
    if (descendants[startId] && descendants[endId]) {
      allHighlightedObjects[linkId] = link;
    }
  }
  return descendants;
};

/**
 * Update the node/link alphas based on the list of highlightedObjects
 *
 * @param {boolean} bHighlight true if highlighting, false if unhighlighting
 * @param {object} highlightedObjects a map (from node/link id to node/link object) containing the objects to highlight
 *
 * @private
 */
dvt.AdfDiagram.prototype._updateAlphas = function(bHighlight, highlightedObjects) {
  // Don't need to worry about max alpha because setting highlightedRowKeys disables all interactive highlighting
  if (this._isFlatDiagram()) {
    // Apply alpha to the links and nodes pane
    var defaultAlpha = bHighlight ? 0.1 : 1.0;
    // Then just reparent the interesting links and nodes
    dvt.CategoryRolloverHandler._updateAlpha([this._linksPane, this._nodesPane], defaultAlpha);
    // First reparent links
    for (var id in highlightedObjects) {
      if (highlightedObjects[id] instanceof dvt.AdfDiagramLink) {
        if (bHighlight) {
          this.moveToDiagramFront(highlightedObjects[id]);
        }
        else {
          this.moveToLinks(highlightedObjects[id]);
        }
      }
    }
    // Then reparent nodes
    for (var id in highlightedObjects) {
      if (highlightedObjects[id] instanceof dvt.AdfDiagramNode) {
        if (bHighlight) {
          this.moveToDiagramFront(highlightedObjects[id]);
        }
        else {
          this.moveToNodes(highlightedObjects[id]);
        }
      }
    }
  }
  else {
    var objects = new Array();
    for (var nodeId in this._nodes)
    {
      var node = this.getNodeById(nodeId);
      objects.push(node);
    }
    for (var linkId in this._links)
    {
      var link = this.getLinkById(linkId);
      objects.push(link);
    }
    for (var i = 0; i < objects.length; i++) {
      var object = objects[i];
      var objectId = object.getData().getId();
      var alpha = (!bHighlight || highlightedObjects[objectId]) ? 1.0 : 0.10;
      dvt.CategoryRolloverHandler._updateAlpha(object.getDisplayables(), alpha);
    }
  }
};

/**
 * Indicates whether this diagram is flat (i.e. has no container nodes)
 *
 * @return {boolean} true if this diagram is flat, false otherwise.
 * @private
 */
dvt.AdfDiagram.prototype._isFlatDiagram = function() {
  return this.getSortedVisibleNodesByGroupDepth().length == 1;
};

dvt.AdfDiagram.prototype._processNodeHighlight = function(node, bOver) {
  if (bOver)
    node.unhighlight();
};

dvt.AdfDiagram.prototype._processLinkHighlight = function(link, bOver) {
  link.unhighlight();
};
/**
 * Show or hide link hover effect
 *
 * @param {dvt.AdfDiagramLink} link the link that has hover effect turned on or off
 * @param {boolean} bOver true if link is hovered over
 * @param {boolean} immediate true if hover effect should occur immediately
 */
dvt.AdfDiagram.prototype.ShowHideLinkHoverEffect = function(link, bOver, immediate)
{
  if (this._ignoreRollover) {
    return;
  }

  if (bOver)
  {
    //if there's not already a mouseover timer running, create a
    //new one and start it
    if (!this._highlightOverTimer)
    {
      if (!immediate) {
        var alphaFunc = function() {
          this.ShowHideLinkHoverEffectHelper(link, bOver);
        };
        this._highlightOverTimer = new dvt.Timer(this.getCtx(), 1000 * dvt.AdfDiagram._HIGHLIGHT_ANIM_DELAY, alphaFunc, this, 1);
        this._highlightOverTimer.start();
      }
      else {
        this.ShowHideLinkHoverEffectHelper(link, bOver);
      }
    }
  }
  else
  {
    //if there's a mouseover timer running, stop it
    if (this._highlightOverTimer)
    {
      this._highlightOverTimer.stop();
      this._highlightOverTimer = null;
    }
    this.ShowHideLinkHoverEffectHelper(link, bOver);
  }
};

dvt.AdfDiagram.prototype.ShowHideLinkHoverEffectHelper = function(link, bOver)
{
  if (this.getSelectionMode()) {
    //set flag on link, because link doesn't do it itself anymore
    link._hover = bOver;
  }

  var bHighlighting = this._linkHighlightBehavior != null && (this._linkHighlightBehavior.indexOf('Link') != -1);

  if (bHighlighting) {
    var highlightedObjects = {};
    highlightedObjects[link.getData().getId()] = link;
    var start = this._linkHighlightBehavior.indexOf('Start') != -1;
    var end = this._linkHighlightBehavior.indexOf('End') != -1;
    if (start) {
      this._addHighlightedObjects(link.getData().getStartId(), highlightedObjects);
    }
    if (end) {
      this._addHighlightedObjects(link.getData().getEndId(), highlightedObjects);
    }
    this._updateAlphas(bOver, highlightedObjects);
  }
};

/**
 * Set layout
 * @param {string} layout  layout name
 */
dvt.AdfDiagram.prototype.setLayout = function(layout)
{
  var layoutChanged = this._layout != layout;
  this._layout = layout;

  if (this._bRendered && layoutChanged)
  {
    var event = dvt.EventFactory.newAdfPropertyChangeEvent(dvt.AdfDiagram.LAYOUT_KEY, layout);
    this.dispatchEvent(event);
    this.UpdateLayout();
    this.UpdateAriaAttributes();
  }
};

/**
 * @protected
 * Update the component layout
 * @param {string} layout  layout name
 */
dvt.AdfDiagram.prototype.UpdateLayout = function(layout)
{
  dvt.AdfDiagramTransitionUtils.processFilter(this);
};

/**
 * Get component layout
 * @param {boolean=} isAuto optional param indicates to get the layout for automation
 * @return {String} component layout
 */
dvt.AdfDiagram.prototype.getLayout = function(isAuto)
{
  return this._layout;
};

dvt.AdfDiagram.prototype.setLayoutCallback = function(layoutCallback)
{
  this._layoutCallback = layoutCallback;
};

dvt.AdfDiagram.prototype.getLayoutCallback = function()
{
  if (!this._layoutCallback)
  {
    this._layoutCallback = new DvtAdfDiagramLayoutCallback();
  }
  return this._layoutCallback;
};

dvt.AdfDiagram.prototype.setLayoutAttributes = function(layoutName, layoutAttrs)
{
  if (!this._layoutAttrs) {
    this._layoutAttrs = {};
  }
  this._layoutAttrs[layoutName] = layoutAttrs;
};

dvt.AdfDiagram.prototype.getLayoutAttributes = function(layoutName)
{
  if (!this._layoutAttrs) {
    return null;
  }
  return this._layoutAttrs[layoutName];
};

dvt.AdfDiagram.prototype.zoomToFit = function() {
  var animator = new dvt.Animator(this.getCtx(), this.getAnimationDuration());
  this.getPanZoomCanvas().zoomToFit(animator, this.GetViewBounds());
  animator.play();
};


/**
 * Zoom and center on the current selection
 */
dvt.AdfDiagram.prototype.zoomAndCenter = function() 
{
  if (this.getPanZoomCanvas())
  {
    this.getPanZoomCanvas().zoomAndCenter();
  }
};


/**
 * Syncs the overview window content and optionally performs a zoom-to-fit
 *
 * @param {dvt.Animator} animator the animator to animate the changes into
 * @param {dvt.Rectangle} fitBounds the dimensions for the zoom-to-fit operation
 * @param {boolean} bDoNotCreateAnimator indicates whether an animator should be created if none was passed in
 * @param {boolean} bSkipZoomToFit indicates whether or not the zoom-to-fit should be skipped
 */
dvt.AdfDiagram.prototype.SyncOverviewAndZoomToFit = function(animator, fitBounds, bDoNotCreateAnimator, bSkipZoomToFit) {
  var bCreatedAnimator = false;
  if (!animator && !bDoNotCreateAnimator) {
    bCreatedAnimator = true;
    animator = new dvt.Animator(this.getCtx(), this.getAnimationDuration());
  }

  var layoutViewport = this.GetLayoutViewport(animator);
  if (layoutViewport)
    fitBounds = layoutViewport;

  //Bug 16689332 - HTML5 HV: NEWLY FETCHED NODE SHOWS WRONG ZOOM FACET
  if (! bSkipZoomToFit) {
    this.getPanZoomCanvas().zoomToFit(animator, fitBounds);
    this._processCenterNode(true, animator);
    this._processPanZoomSettings(true, animator);
  }
  if (this._overviewWindow) {
    DvtAdfDiagramOverviewUtils.SyncOverviewContent(this, animator);
  }

  if (bCreatedAnimator)
    animator.play();
};

/**
 * @protected
 * Sort the nodes by group
 * @param {boolean} bAllNodes true to include invisible nodes and false to exclude invisible nodes
 * @return {array} sorted array of nodes
 */
dvt.AdfDiagram.prototype.SortNodesByGroupDepth = function(bAllNodes)
{
  var nodesByGroupDepth = [];

  var node;
  for (var i = 0; i < this._rootNodes.length; i++)
  {
    node = this.getNodeById(this._rootNodes[i]);
    if (!bAllNodes && !node.getVisible()) {
      continue;
    }

    if (nodesByGroupDepth.length < 1)
    {
      nodesByGroupDepth.push([]);
    }

    nodesByGroupDepth[0].push(node);
  }

  if (nodesByGroupDepth.length > 0)
  {
    this._sortNodesByGroupDepthHelper(nodesByGroupDepth, nodesByGroupDepth[0], 1, bAllNodes);
  }

  return nodesByGroupDepth;
};

/**
 * @private
 * Sort the nodes by group helper
 * @param {array} nodesByGroupDepth  Nodes to sort by group
 * @param {array} arPrevDepthNodes  sorted nodes at current depth
 * @param {number} depth  current depth
 * @param {boolean} bAllNodes true to include invisible nodes and false to exclude invisible nodes
 */
dvt.AdfDiagram.prototype._sortNodesByGroupDepthHelper = function(nodesByGroupDepth, arPrevDepthNodes, depth, bAllNodes)
{
  var childrenAtCurrentDepth = false;
  for (var j = 0; j < arPrevDepthNodes.length; j++)
  {
    var node = arPrevDepthNodes[j];
    var arChildIds = node.getChildNodeIds();
    if (arChildIds && arChildIds.length > 0 &&
        (node.isDisclosed() || bAllNodes))
    {
      for (var i = 0; i < arChildIds.length; i++)
      {
        var childNode = this.getNodeById(arChildIds[i]);
        if (!bAllNodes && !childNode.getVisible()) {
          continue;
        }
        if (nodesByGroupDepth.length <= depth)
        {
          childrenAtCurrentDepth = true;
          nodesByGroupDepth.push([]);
        }
        var arChildNodes = nodesByGroupDepth[depth];
        arChildNodes.push(childNode);
      }
    }
  }
  if (childrenAtCurrentDepth)
  {
    this._sortNodesByGroupDepthHelper(nodesByGroupDepth, nodesByGroupDepth[depth], depth + 1, bAllNodes);
  }
};

dvt.AdfDiagram.prototype.IsSkinAlta = function() {
  return (this._skinName == dvt.CSSStyle.SKIN_ALTA);
};

dvt.AdfDiagram.prototype.UsePanelDrawer = function() {
  return true;
};

/**
 * @override
 */
dvt.AdfDiagram.prototype.PreRender = function() {
  dvt.AdfDiagram.superclass.PreRender.call(this);
  //Bug 15838158: if resizing the component while in stack preview mode, close the preview
  //before rendering and then show it again afterwards
  this._hiddenStackPreviewNode = null;
  if (this.IsResize()) {
    var stackPreviewNodeId = this.GetStackPreviewNodeId();
    if (stackPreviewNodeId) {
      this._hiddenStackPreviewNode = this.getNodeById(stackPreviewNodeId);
      this._hiddenStackPreviewNode.HideStackPreview(true);
    }
  }
};

/**
 * @override
 */
dvt.AdfDiagram.prototype.Render = function() {
  if (!this._handledEventXml) {
    dvt.AdfDiagram.superclass.Render.call(this);
    this.InitComponentInternal();
    this.RenderComponentInternal();
  }
  this._handledEventXml = false;
  if (this._hiddenStackPreviewNode) {
    this._hiddenStackPreviewNode.ShowStackPreview(true);
    this._hiddenStackPreviewNode = null;
  }
  //Clear the automation states initially
  this.clearAutomationState();
};

/**
 * @override
 */
dvt.AdfDiagram.prototype.SetOptions = function(options) {
  dvt.AdfDiagram.superclass.SetOptions.call(this, options);
  var parser = this.GetJsonParser();
  parser.loadJsonInitial(this.Options);
};


/**
 * @protected
 * Called by the renderComponent() method. Used to initialize or reinitialize the component before rendering.
 */
dvt.AdfDiagram.prototype.InitComponentInternal = function() {
  dvt.AdfDiagram.superclass.InitComponentInternal.call(this);

  var currDisplayedPanel = null;
  var isPanelDrawerRendered = false;
  if (this._panelDrawer) {
    isPanelDrawerRendered = true;
    if (this._panelDrawer.isDisclosed())
      currDisplayedPanel = this._panelDrawer.getDisplayedPanelId();
    this._panelDrawer.destroy();
    this.removeChild(this._panelDrawer);
    this._panelDrawer = null;
  }

  if (this._overviewWindowPanel) {
    this._overviewWindowPanel.destroy();
    this.removeChild(this._overviewWindowPanel);
    this._overviewWindowPanel = null;
  }

  this.AddPanelDrawer(isPanelDrawerRendered, currDisplayedPanel);
  if (this.hasSpaceForOverviewWindowPanel() && this._overviewWindow) {
    this.AddOverviewWindow();
  }

  //add context keyboard handlers
  this.setContextKeyboardHandlers();
};

/**
 * Creates the subcomponents for this component and adds them as panels to a dvt.PanelDrawer.
 * @param {boolean} isPanelDrawerRendered Flag that indicates whether the panel drawer was already rendered
 * @param {string} currDisplayedPanel Id of the dispalyed panel. It could be null if all the tabs are collapsed or panel drawer was not rendered
 * @protected
 */
dvt.AdfDiagram.prototype.AddPanelDrawer = function(isPanelDrawerRendered, currDisplayedPanel) {

  var upState, overState, downState, tip, searchPanel;
  var bPalette = (this._paletteJson) && (!this.isReadOnly() && (this.isNodeCreateEnabled() || this.isLinkCreateEnabled()));
  if (this._searchPanel &&
      (this._searchPanel.getInitialBehavior() != dvt.PanZoomComponent.CONTROL_PANEL_BEHAVIOR_HIDDEN)) {
    searchPanel = this._searchPanel;
  }

  if (this.UsePanelDrawer() && (this._legendJson || bPalette || searchPanel)) {
    this._panelDrawer = this.CreatePanelDrawer('pd');
    // Note: we stil need the newDisplayedPanel to be set even if the panel prawer was previously rendered
    // and the currDisplayedPanel has a value. We need it for the case if the currDisplayedPanel is an overview window
    // and it will not be a part of panel drawer
    var newDisplayedPanel;

    if (searchPanel) {
      //Padding is set in search panel for Alta Skin.
      if (this.IsSkinAlta()) {
        this._panelDrawer.setContentPadding(0);
        searchPanel.setButtonStyle(this.getButtonStyle());
        searchPanel.setLabelStyle(this.getLabelStyle());
      }
      searchPanel.setResultsMaxWidth(this._panelDrawer.getMaxContentWidth());
      searchPanel.setResultsMaxHeight(this._panelDrawer.getMaxContentHeight());
      searchPanel.setVisible(false);
      searchPanel.setState(dvt.SearchPanel.STATE_EMBEDDED);
      this.RenderSearchPanel();
      this._panelDrawer.setMaxContainerHeight(searchPanel.getDimensions().h);
      searchPanel.getParent().removeChild(searchPanel);
      searchPanel.setVisible(true);

      upState = new dvt.Image(this.getCtx(), this.getResourcesMap()[dvt.PanelDrawer.PANEL_SEARCH_ICON_ENA], 0, 0, dvt.PanelDrawer.IMAGE_SIZE, dvt.PanelDrawer.IMAGE_SIZE);
      overState = new dvt.Image(this.getCtx(), this.getResourcesMap()[dvt.PanelDrawer.PANEL_SEARCH_ICON_OVR], 0, 0, dvt.PanelDrawer.IMAGE_SIZE, dvt.PanelDrawer.IMAGE_SIZE);
      downState = new dvt.Image(this.getCtx(), this.getResourcesMap()[dvt.PanelDrawer.PANEL_SEARCH_ICON_DWN], 0, 0, dvt.PanelDrawer.IMAGE_SIZE, dvt.PanelDrawer.IMAGE_SIZE);
      tip = dvt.Bundle.getTranslatedString(dvt.Bundle.SUBCOMPONENT_PREFIX, 'SEARCH');

      this._panelDrawer.addPanel(searchPanel, upState, overState, downState, tip, dvt.PanelDrawer.PANEL_SEARCH);
      if (!newDisplayedPanel && searchPanel.getInitialBehavior() == dvt.PanZoomComponent.CONTROL_PANEL_BEHAVIOR_INIT_EXPANDED) {
        newDisplayedPanel = dvt.PanelDrawer.PANEL_SEARCH;
      }
    }

    if (bPalette) {
      this._palette = new dvt.Palette(this.getCtx(), 'palette', this._panelDrawer.getMaxContentWidth(), this._panelDrawer.getMaxContentHeight(), this.handlePaletteEvent, this, this._dragSource, this.getResourcesMap(), this.getPaletteStyles());
      this._palette.setJSON(this._paletteJson);

      this.addChild(this._palette);
      this._palette.render();
      this._panelDrawer.setMaxContainerHeight(this._palette.getExpandedHeight());
      this.removeChild(this._palette);

      upState = new dvt.Image(this.getCtx(), this.getResourcesMap()[dvt.PanelDrawer.PANEL_PALETTE_ICON_ENA], 0, 0, dvt.PanelDrawer.IMAGE_SIZE, dvt.PanelDrawer.IMAGE_SIZE);
      overState = new dvt.Image(this.getCtx(), this.getResourcesMap()[dvt.PanelDrawer.PANEL_PALETTE_ICON_OVR], 0, 0, dvt.PanelDrawer.IMAGE_SIZE, dvt.PanelDrawer.IMAGE_SIZE);
      downState = new dvt.Image(this.getCtx(), this.getResourcesMap()[dvt.PanelDrawer.PANEL_PALETTE_ICON_DWN], 0, 0, dvt.PanelDrawer.IMAGE_SIZE, dvt.PanelDrawer.IMAGE_SIZE);
      tip = dvt.Bundle.getTranslatedString(dvt.Bundle.SUBCOMPONENT_PREFIX, 'PALETTE');

      this._panelDrawer.addPanel(this._palette, upState, overState, downState, tip, dvt.PanelDrawer.PANEL_PALETTE);
      if (!newDisplayedPanel && this._palette.isDisclosed()) {
        newDisplayedPanel = dvt.PanelDrawer.PANEL_PALETTE;
      }
    }

    if (this._legendJson) {
      var legendData = this._legendJson;
      this._legend = dvt.Legend.newInstance(this.getCtx(), this.processLegendEvent, this);
      this.addChild(this._legend);
      var legendMaxWidth = this._getLegendMaxWidthValue(legendData['maxWidth'], this.getWidth());
      var preferredSize = this._legend.getPreferredSize(legendData, legendMaxWidth, this._panelDrawer.getMaxContentHeight());
      this._legend.render(legendData, preferredSize.w, preferredSize.h);
      this._panelDrawer.setMaxContainerHeight(this._legend.getContentDimensions().h);
      this.removeChild(this._legend);

      upState = new dvt.Image(this.getCtx(), this.getResourcesMap()[dvt.PanelDrawer.PANEL_LEGEND_ICON_ENA], 0, 0, dvt.PanelDrawer.IMAGE_SIZE, dvt.PanelDrawer.IMAGE_SIZE);
      overState = new dvt.Image(this.getCtx(), this.getResourcesMap()[dvt.PanelDrawer.PANEL_LEGEND_ICON_OVR], 0, 0, dvt.PanelDrawer.IMAGE_SIZE, dvt.PanelDrawer.IMAGE_SIZE);
      downState = new dvt.Image(this.getCtx(), this.getResourcesMap()[dvt.PanelDrawer.PANEL_LEGEND_ICON_DWN], 0, 0, dvt.PanelDrawer.IMAGE_SIZE, dvt.PanelDrawer.IMAGE_SIZE);
      tip = dvt.Bundle.getTranslatedString(dvt.Bundle.SUBCOMPONENT_PREFIX, 'LEGEND');

      this._panelDrawer.addPanel(this._legend, upState, overState, downState, tip, dvt.PanelDrawer.PANEL_LEGEND);
      if (!newDisplayedPanel && legendData['disclosed'] == 'true') {
        newDisplayedPanel = dvt.PanelDrawer.PANEL_LEGEND;
      }
    }

    var displayedPanel = null;
    var bOrigDisplayedPanelExists = true;
    if (!this.hasSpaceForOverviewWindowPanel() && this._overviewWindow) {
      this.AddOverviewWindowTab(this._panelDrawer);
      this._refreshOverviewContent = true;
      var viewport = DvtAdfDiagramOverviewUtils.CalcAdjustedViewport(this, null);
      this._overviewWindow.setViewportDimensions(viewport);
      this._overviewWindowPanel = this._panelDrawer;
    }
    else if (currDisplayedPanel === dvt.PanelDrawer.PANEL_OVERVIEW)
      bOrigDisplayedPanelExists = false;

    // finally decide what panel to show
    if (!isPanelDrawerRendered)
      displayedPanel = newDisplayedPanel;
    else if (isPanelDrawerRendered && !currDisplayedPanel) //panel drawer was rendered, but all the tabs are collapsed
      displayedPanel = null;
    else if (isPanelDrawerRendered && currDisplayedPanel && bOrigDisplayedPanelExists) // panel drawer was rendered, one panel was expanded and that panel is still there
      displayedPanel = currDisplayedPanel;
    else if (isPanelDrawerRendered && currDisplayedPanel && !bOrigDisplayedPanelExists) // panel drawer was rendered, one panel was expanded and that panel is not there
      displayedPanel = newDisplayedPanel;

    this._panelDrawer.renderComponent();
    if (displayedPanel) {
      this._panelDrawer.setDisplayedPanelId(displayedPanel);
      if (!this._panelDrawer.isDisclosed()) {
        this._panelDrawer.setDisclosed(true, true);
      }
    }
  }
};


dvt.AdfDiagram.prototype.hasSpaceForOverviewWindowPanel = function() {
  var bHasSpaceForOviewviewPanel = true;
  var height = this.getHeight();

  if (this._panelDrawer) {
    if (this._panelDrawer.getMaxContainerHeight() + dvt.AdfDiagram.OVERVIEW_MAX_HEIGHT >= height)
      bHasSpaceForOviewviewPanel = false;
  }
  return bHasSpaceForOviewviewPanel;
};


/**
 * Renders a dvt.OverviewWindow within a dvt.PanelDrawer and adds it to this component.
 * @protected
 */
dvt.AdfDiagram.prototype.AddOverviewWindow = function() {

  var overview = this._overviewWindow;
  if (overview) {
    if (!this._overviewWindowPanel) {
      this._overviewWindowPanel = this.CreateOverviewWindowPanel();
      this.PositionOverviewWindowPanel();
      this.AddOverviewWindowTab(this._overviewWindowPanel);

      this._overviewWindowPanel.renderComponent();
      this._overviewWindowPanel.setDisplayedPanelId(dvt.PanelDrawer.PANEL_OVERVIEW);
      this._overviewWindowPanel.setDisclosed(this._overviewWindow.isDisclosed(), true);
      this._refreshOverviewContent = true;
    }
    // update viewport size
    var viewport = DvtAdfDiagramOverviewUtils.CalcAdjustedViewport(this, null);
    this._overviewWindow.setViewportDimensions(viewport);
  }
};


/**
 * Creates a panel for this component's dvt.OverviewWindow to be added to the passed in dvt.PanelDrawer.
 * @param {dvt.PanelDrawer} panelDrawer The dvt.PanelDrawer to add the overview window panel to
 * @protected
 */
dvt.AdfDiagram.prototype.AddOverviewWindowTab = function(panelDrawer) {
  //temporarily hide the panel and add it to the display list so
  //that we can render, and then remove it from the display list
  this._overviewWindow.setVisible(false);
  this.addChild(this._overviewWindow);
  this._overviewWindow.render();
  this.removeChild(this._overviewWindow);
  this._overviewWindow.setVisible(true);

  var upState = new dvt.Image(this.getCtx(), this.getResourcesMap()[dvt.PanelDrawer.PANEL_OVERVIEW_ICON_ENA], 0, 0, dvt.PanelDrawer.IMAGE_SIZE, dvt.PanelDrawer.IMAGE_SIZE);
  var overState = new dvt.Image(this.getCtx(), this.getResourcesMap()[dvt.PanelDrawer.PANEL_OVERVIEW_ICON_OVR], 0, 0, dvt.PanelDrawer.IMAGE_SIZE, dvt.PanelDrawer.IMAGE_SIZE);
  var downState = new dvt.Image(this.getCtx(), this.getResourcesMap()[dvt.PanelDrawer.PANEL_OVERVIEW_ICON_DWN], 0, 0, dvt.PanelDrawer.IMAGE_SIZE, dvt.PanelDrawer.IMAGE_SIZE);
  var tip = dvt.Bundle.getTranslatedString(dvt.Bundle.SUBCOMPONENT_PREFIX, 'OVERVIEW');
  panelDrawer.addPanel(this._overviewWindow, upState, overState, downState, tip, dvt.PanelDrawer.PANEL_OVERVIEW);
};

dvt.AdfDiagram.prototype.CreateOverviewWindowPanel = function() {
  var overviewWindowPanel = new dvt.PanelDrawer(this.getCtx(), null, this, 'ov');
  overviewWindowPanel.addEvtListener(dvt.PanelDrawerEvent.TYPE, this.HandlePanelDrawerEvent, false, this);
  overviewWindowPanel.setDockSide(dvt.PanelDrawer.DOCK_BOTTOM);
  overviewWindowPanel.setMaxWidth(dvt.AdfDiagram.OVERVIEW_MAX_WIDTH);
  overviewWindowPanel.setMaxHeight(dvt.AdfDiagram.OVERVIEW_MAX_HEIGHT);
  this.addChild(overviewWindowPanel);
  return overviewWindowPanel;
};

dvt.AdfDiagram.prototype.CreatePanelDrawer = function(sid) {
  this._panelDrawer = new dvt.PanelDrawer(this.getCtx(), null, this, sid);
  this._panelDrawer.addEvtListener(dvt.PanelDrawerEvent.TYPE, this.HandlePanelDrawerEvent, false, this);
  this.addChild(this._panelDrawer);
  this.PositionPanelDrawer();
  return this._panelDrawer;
};

dvt.AdfDiagram.prototype.PositionOverviewWindowPanel = function() {
  if (this._overviewWindowPanel) {
    this._overviewWindowPanel.setTranslateY(this.getHeight());
    if (!dvt.Agent.isRightToLeft(this.getCtx())) {
      this._overviewWindowPanel.setTranslateX(this.getWidth());
    }
    else {
      this._overviewWindowPanel.setDiscloseDirection(dvt.PanelDrawer.DIR_RIGHT);
      this._overviewWindowPanel.setTranslateX(0);
    }
  }
};

dvt.AdfDiagram.prototype.PositionPanelDrawer = function() {
  this._panelDrawer.setMaxWidth(.5 * this.getWidth());
  this._panelDrawer.setMaxHeight(.8 * this.getHeight());
  if (!dvt.Agent.isRightToLeft(this.getCtx())) {
    this._panelDrawer.setTranslateX(this.getWidth());
  }
  else {
    this._panelDrawer.setDiscloseDirection(dvt.PanelDrawer.DIR_RIGHT);
    this._panelDrawer.setTranslateX(0);
  }
};

/**
 * Render and position diagram nodes and links
 * @param {dvt.Animator} animator Optional animator for the component that is used to animate transition from an old state to a new one
 * @protected
 */
dvt.AdfDiagram.prototype.ProcessNodesAndLinks = function(animator) {
  if ((!this._bRendered && !this._bCrossedZoomThreshold) || this._renderZoom100Nodes) {
    if (! this._renderZoom100Nodes) {
      this._processHighlighting();
    }

    var nodesByGroupDepth = this.SortNodesByGroupDepth(true);
    for (var depth = nodesByGroupDepth.length - 1; depth >= 0; depth--) {
      //Performance: we need to use the afContext for parsing, stamping
      //sizing and layout.
      var afContext = this.createAfContext();
      var arNodes = nodesByGroupDepth[depth];
      this.CreateNodeContentsAtDepth(arNodes, afContext, animator, true);
      //NOTE: make sure the dom is not changing during this call.
      this.RenderNodesAtDepth(arNodes, afContext, animator, true);
    }

    if (! this._renderZoom100Nodes) {
      this.SetInitialVisibility();
    }
  }

  this.RenderLinks();
  if (this._layoutBehavior === 'global') {
    this.LayoutNodesWithGlobalLayout(animator);
  }
  else {
    this.resetSortedNodesArray();
    var nodesByGroupDepth = this.getSortedVisibleNodesByGroupDepth();
    for (var depth = nodesByGroupDepth.length - 1; depth >= 0; depth--)
    {
      //Performance: we need to use the afContext for parsing, stamping
      //sizing and layout.
      var afContext = this.createAfContext();
      var arNodes = nodesByGroupDepth[depth];
      this.CreateNodeContentsAtDepth(arNodes, afContext, animator);
      //NOTE: make sure the dom is not changing during this call.
      this.RenderNodesAtDepth(arNodes, afContext, animator);
      // changing zoom facets doesn't require running layout
      if (!this._bCrossedZoomThreshold) {
        this.LayoutNodesAtDepth(nodesByGroupDepth, depth, animator);
      }
    }
  }
};

/**
 * Renders a Diagram component after it was initialized.
 * @param {dvt.Animator} animator Optional animator for the component that is used to animate transition from an old state to a new one
 * @protected
 */
dvt.AdfDiagram.prototype.RenderComponentInternal = function(animator)
{
  this.setInteractionEnabled(false);
  var emptyDiagram = this.GetAllNodes().length == 0;
  if (!emptyDiagram) {
    if (!this._bRendered) {
      var buttonStyles = this.getDiagramButtonStyles();
      this._buttonBorderRadius = dvt.AdfDiagram.getButtonBorderRadius(buttonStyles);
    }
    if (!this._bAnimTransition) { // the layout viewport should be preserved for animation step
      this.ClearLayoutViewport();
    }
    this.ProcessNodesAndLinks(animator);
    var calcViewBounds = this._cachedViewBounds == null;
    if (calcViewBounds) {
      this._cachedViewBounds = this.GetViewBounds(animator);
    }

    if (this._overviewWindow && this._refreshOverviewContent) {
      var ovContent = DvtAdfDiagramOverviewUtils.CreateOverviewContent(this, this._cachedViewBounds, this.getOverviewWindowContentStyle());
      this._overviewWindow.setContent(ovContent);
      this._refreshOverviewContent = false;
    }

    // if this is a component resize, recenter the diagram but do not re-layout or zoom
    var pzc = this.getPanZoomCanvas();
    if (this.IsResize() && !this.IsLayoutViewport()) {
      // Update the min zoom if it's unspecified
      var viewBounds = this.AdjustMinZoom(animator, this._cachedViewBounds);
      if (!viewBounds) {
        viewBounds = this.GetViewBounds();
      }
      pzc.center(null, viewBounds);
    }

    // Update the selection manager with the initial selections.  This must be done after
    // the shapes are created to apply the selection effects.
    this._processInitialSelections();

    if ((!this._bRendered && !this._bCrossedZoomThreshold) || this._renderZoom100Nodes) {
      this._bInitDisclosedNodes = false;
      this.AdjustMinZoom(animator, this._cachedViewBounds);

      //if we're rendering null xml, it may be for a maximize/restore,
      //so don't automatically zoom-to-fit
      if (!this.IsResize()) {

        //Bug 16695081: don't override a viewport returned from the layout engine
        var bLayoutViewport = this.IsLayoutViewport();
        var fitBounds = bLayoutViewport ? this.GetLayoutViewport() : this._cachedViewBounds;
        if (bLayoutViewport)
          pzc.setZoomToFitPadding(0);

        if (! this._renderZoom100Nodes &&           //skip zoomToFit if the component will be rerendered when images are loaded
            (! this._panZoom || bLayoutViewport)) { //skip zoomToFit if we are going to apply panzoom settings later in _processPanZoomSettings
          //BUG 16962365 - DIAGRAM/NODE/PANELCARD LOST THEIR STATE AFTER IMAGES WERE LOADED
          pzc.zoomToFit(null, fitBounds);
        }
      }

      if (this._syncOverviewAndPanZoom && this._overviewWindow) {
        //sync panzoom and overview window after images are loaded
        DvtAdfDiagramOverviewUtils.ZoomToFitOverviewContent(this, this._cachedViewBounds, animator);
        pzc.zoomTo(pzc.getZoom(), pzc.getPanX(), pzc.getPanY(), animator);
        pzc.panTo(pzc.getPanX(), pzc.getPanY(), animator);
      }

      var clearInitialSettings = this._bAnimTransition || (this._imageNotReadyList && this._imageNotReadyList.length > 0) ? false : true;
      this._processCenterNode(clearInitialSettings, animator);
      this._processPanZoomSettings(clearInitialSettings, animator);
      pzc.setPanningEnabled(this._panningEnabled);
      pzc.setPanDirection(this.getPanDirection());
      pzc.setZoomingEnabled(this._zoomingEnabled);
      pzc.setZoomToFitEnabled(this._zoomingEnabled);

      //only do initial animation if this is the initial render
      //Bug 14813478: don't do init anim when the component is PPRed
      if (dvt.Agent.isEnvironmentBrowser() && this.getInitAnim() && this.IsInitAnimEnabled()) {
        var dia = this.GetInitAnimHelper();

        //fade in overview window after initial animation
        if (this._overviewWindow) {
          this._overviewWindowPanel.setAlpha(0);
          var ovFadeInAnim = new dvt.AnimFadeIn(this.getCtx(),
              this._overviewWindowPanel, this.getAnimationDuration());
          dia.setOnEnd(ovFadeInAnim.play, ovFadeInAnim);
        }

        dia.play(this.getInitAnim());
      }

      // Animate the nodes and links transition from old state to new after a PPR.
      // The _bAnimTransition flag will be set to true when a peer reinitializes the component on data change
      // by calling prepareForDataChange(). See dvt.AdfDiagram.prototype.prepareForDataChange()
      if (this._bAnimTransition) {
        this._bAnimTransition = false;
        dvt.AdfDiagramTransitionUtils.__animateTransition(this, true);
        this.ClearLayoutViewport();
      }
    }

    if (animator) {
      dvt.Playable.appendOnEnd(animator, this._toggleRestoreButton, this);
    }
    else {
      this._toggleRestoreButton();
    }

    this.UpdateDrillBreadcrumbs(animator);
    this.UpdateKeyboardFocus();

    this._bRendered = true;
    this._currentViewport = null;

    if (!this.UsePanelDrawer()) {
      if (this.getSearchPanel() &&
          (this.getSearchPanel().getInitialBehavior() != dvt.PanZoomComponent.CONTROL_PANEL_BEHAVIOR_HIDDEN)) {
        this.RenderSearchPanel();
      }
      else {
        this.RemoveSearchPanel();
      }
    }
  }
  this.RefreshEmptyText(emptyDiagram);

  // Constrain pan bounds
  if (this.IsPanningEnabled()) {
    var contentDim = this._cachedViewBounds;

    if (contentDim != null) {
      var zoom = this.getPanZoomCanvas().getZoom(animator);
      this.ConstrainPanning(contentDim.x, contentDim.y, contentDim.w, contentDim.h, zoom);
    }
  }

  this._renderZoom100Nodes = null;
  this._bAfImageLoaded = false;

  //Process the stack preview
  if (!animator)
    this.processPreviewStack(this);

  if (calcViewBounds) {
    this._cachedViewBounds = null;
  }
  this.setInteractionEnabled(true);
  /*
  var endTime = new Date().getTime();
  var diffTime = endTime - startTime;

  logText("renderComponent elapsed ms = " + diffTime);
  */
};


/**
 * Process the preview Stack based on the selected node id.
 */
dvt.AdfDiagram.prototype.processPreviewStack = function() {
  if (this._stackPreviewNode) {
    var previewNode = this.getNodeById(this._stackPreviewNode);
    if (previewNode) {
      previewNode._stackBadgeHandler.ShowDialog(null, true);
    }
    if (this._stackPreviewPanZoom) {
      previewNode._stackBadgeHandler.ApplyLayoutPanZoom(this._stackPreviewPanZoom);
      this._stackPreviewPanZoom = null;
    }
    this._stackPreviewNode = null;
  }
};


dvt.AdfDiagram.prototype.SetInitialVisibility = function() {
  dvt.AdfDiagramTransitionUtils.setVisibility(this);
};


/**
 * @private
 */
dvt.AdfDiagram.prototype._toggleRestoreButton = function() {
  if ((this._isolatedNodes && this._isolatedNodes.length > 0)
      //don't need Show All button for drill, because we have breadcrumbs
      //     || this._drilledNodePath
  )
  {
    this.ShowRestoreButton();
  }
  else
  {
    this.HideRestoreButton();
  }
};


/**
 * Update the selection handler with the initial selections.
 * @private
 */
dvt.AdfDiagram.prototype._processInitialSelections = function() {
  if (this._selectionHandler && (this._selectedNodes || this._selectedLinks)) {
    var targets = new Array();
    for (var nodeId in this._nodes)
    {
      var node = this.getNodeById(nodeId);
      node.setSelected(false);     //Unselect nodes by default
      targets.push(node);
    }

    for (var linkId in this._links)
    {
      var link = this.getLinkById(linkId);
      link.setSelected(false);     //Unselect links by default
      targets.push(link);
    }
    var selection = new Array();
    if (this._selectedNodes) {
      for (var index in this._selectedNodes) {
        selection.push(this._selectedNodes[index]);
      }
    }
    if (this._selectedLinks) {
      for (var index in this._selectedLinks) {
        selection.push(this._selectedLinks[index]);
      }
    }
    this._selectionHandler.processInitialSelections(selection, targets);
    this._selectedNodes = null;
    this._selectedLinks = null;
  }
};


/**
 * @private
 * Process highlighted nodes and links
 */
dvt.AdfDiagram.prototype._processHighlighting = function() {
  if (this._highlightedNodes || this._highlightedLinks) {
    var highlighted = {};
    if (this._highlightedNodes) {
      for (var i = 0; i < this._highlightedNodes.length; i++) {
        highlighted[this._highlightedNodes[i]] = this._highlightedNodes[i];
      }
    }
    if (this._highlightedLinks) {
      for (var i = 0; i < this._highlightedLinks.length; i++) {
        highlighted[this._highlightedLinks[i]] = this._highlightedLinks[i];
      }
    }
    for (var nodeId in this._nodes) {
      var node = this.getNodeById(nodeId);
      node.SetMaxAlpha(highlighted[nodeId] ? 1.0 : 0.10);
    }

    for (var linkId in this._links) {
      var link = this.getLinkById(linkId);
      link.SetMaxAlpha(highlighted[linkId] ? 1.0 : 0.10);
    }
  }
};

/**
 * Render links
 * @protected
 */
dvt.AdfDiagram.prototype.RenderLinks = function() {
  for (var linkId in this._links) {
    var link = this.getLinkById(linkId);
    if (link.getVisible()) {
      // Links will short-circuit if they don't need to render
      link.renderLink();
    }
  }
};

//Performance: need to use the same afContext when creating afRoot
dvt.AdfDiagram.prototype.RenderNodesAtDepth = function(arNodes, afContext, animator, bZoom100)
{
  var oZoomFlag;
  for (var i = 0; i < arNodes.length; i++)
  {
    var node = arNodes[i];

    if (this._renderZoom100Nodes && bZoom100) {
      if (this._renderZoom100Nodes[node.getData().getId()]) {
        // save zoom flag
        oZoomFlag = node.getForceZoom100Facet();
        node.setForceZoom100Facet(true);

        node.ClearNodeSize();

        //Bug 16953938 - SIZE OF THE DIAGRAM NODE UNDERLAY IS WRONG
        //if a container node was disclosed at zoom100, we need to re-render
        //it in collapsed state or its underlay wont size correctly when it's collapsed
        if (node.isDisclosed()) {
          node.setDisclosed(false);
          node.renderNode(afContext, animator);
          node.setDisclosed(true);
        }
        else {
          node.renderNode(afContext, animator);
        }
        // restore zoom flag
        node.setForceZoom100Facet(oZoomFlag);
      }
    }
    else {
      node.renderNode(afContext, animator);
    }
  }
};

/**
 * Layout nodes using global layout
 * @param {dvt.Animator} animator Optional animator for the component that is used to animate transition from an old state to a new one
 * @protected
 */
dvt.AdfDiagram.prototype.LayoutNodesWithGlobalLayout = function(animator) {

  var layout = this.getLayout();
  var layoutContext = this.CreateLayoutContext(layout);

  // add all nodes starting with root nodes
  for (var i = 0; i < this._rootNodes.length; i++) {
    var rootNode = this.getNodeById(this._rootNodes[i]);
    var lcRootNode = this.CreateLayoutContextNode(rootNode, layout, true, layoutContext);
    this._addChildren(rootNode, lcRootNode, layout, layoutContext);
    layoutContext.addNode(lcRootNode);
  }
  // add all links
  for (var linkId in this._links) {
    var link = this.getLinkById(linkId);
    if (!link.getVisible())
      continue;
    var parentId = link.getData().getStartId();
    var childId = link.getData().getEndId();
    if (this._containerLinkPromotionMap && this._containerPromotionLinkMap) {
      var promotion = this._containerLinkPromotionMap[linkId];
      if (promotion) {
        parentId = promotion[0];
        childId = promotion[1];
      }
    }

    if (this.getNodeById(parentId).getVisible() && this.getNodeById(childId).getVisible()) {
      var lc = this.CreateLayoutContextLink(link, parentId, childId, layout, layoutContext);
      layoutContext.addLink(lc);
    }
  }


  if (layoutContext.getNodeCount() > 0) {
    layoutContext = this.getLayoutCallback().doLayout(this, layoutContext);
    if (layoutContext) {
      //render nodes if they not rendered yet
      var rootNodesCount = layoutContext.getNodeCount();
      for (i = 0; i < rootNodesCount; i++) {
        var rootNode = layoutContext.getNodeByIndex(i);
        if (!rootNode.IsRendered)
          this.renderNodeFromContext(rootNode);
      }
      this.ApplyLayoutContext(layoutContext, animator);
    }
  }
};


/**
 * Helper function that addes child context nodes to the parent context node
 * @param {DvtDiagramNode} parentNode diagram node
 * @param {DvtDiagramLayoutContextNode} lcParentNode parent context node
 * @param {string} layout name of the layout for the node
 * @param {object} layoutContext  layout context
 * @private
 */
dvt.AdfDiagram.prototype._addChildren = function(parentNode, lcParentNode, layout, layoutContext) {
  if (parentNode.isDisclosed()) {
    var arChildIds = parentNode.getChildNodeIds();
    var lcChildNodes = [];
    for (var j = 0; j < arChildIds.length; j++) {
      var childNode = this.getNodeById(arChildIds[j]);
      if (childNode.getVisible()) {
        var lcChildNode = this.CreateLayoutContextNode(childNode, layout, true, layoutContext);
        layoutContext.addNodeToMap(lcChildNode);
        lcChildNode.setParentNode(lcParentNode);
        lcChildNodes.push(lcChildNode);
        this._addChildren(childNode, lcChildNode, null, layoutContext);
      }
    }
    lcParentNode.setChildNodes(lcChildNodes);
  }
};


dvt.AdfDiagram.prototype.LayoutNodesAtDepth = function(nodesByGroupDepth, depth, animator)
{
  var arNodes = nodesByGroupDepth[depth];
  var nodesByGroup = {};
  for (var i = 0; i < arNodes.length; i++)
  {
    var node = arNodes[i];

    if (!node.getVisible())
    {
      continue;
    }

    var groupId = node.getData().getGroupId();
    if (!groupId)
    {
      groupId = dvt.AdfDiagram.UNGROUPED_ID;
    }
    if (!nodesByGroup[groupId])
    {
      nodesByGroup[groupId] = [];
    }
    nodesByGroup[groupId].push(node);
  }

  for (var gId in nodesByGroup)
  {
    this.LayoutNodes(gId, nodesByGroup[gId],
                     this.GetLayoutAppliedToNode(nodesByGroup[gId][0]),
                     animator);
  }
};


/**
 * @protected
 * Helper method that positions diagram nodes
 * @param {string} groupId Container id for the nodes
 * @param {array} arNodes Array of the diagram nodes to be positioned
 * @param {string} layout A name of the layout that is going to be used to position the nodes
 * @param {dvt.Animator} animator The animator to animate the changes into
 */
dvt.AdfDiagram.prototype.LayoutNodes = function(groupId, arNodes, layout, animator)
{
  if (this.getLayoutCallback())
  {
    //Bug 13868644: don't pass the container node's layout
    //attributes as the global layout attributes when laying out
    //the nodes in the container
    var containerId = groupId;
    if (containerId == dvt.AdfDiagram.UNGROUPED_ID) {
      containerId = null;
    }
    var layoutContext = this.CreateLayoutContext(layout, null, containerId);
    var nodeIds = {};
    for (var n = 0; n < arNodes.length; n++)
    {
      layoutContext.addNode(this.CreateLayoutContextNode(arNodes[n], layout, false, layoutContext));
      nodeIds[arNodes[n].getData().getId()] = true;
    }

    var groupNode = null;
    var bSaveOffset = true;
    if (groupId != dvt.AdfDiagram.UNGROUPED_ID) {
      groupNode = this.getNodeById(groupId);
      bSaveOffset = false;
    }
    var arCrossContainerLinkIds = null;

    if (!groupNode)
    {
      arCrossContainerLinkIds = this._arCrossContainerLinkIds;
    }
    else
    {
      arCrossContainerLinkIds = groupNode.getCrossChildLinkIds();
    }

    for (var linkId in this._links)
    {
      var link = this.getLinkById(linkId);
      if (!link.getVisible() || (arCrossContainerLinkIds && dvt.ArrayUtils.getIndex(arCrossContainerLinkIds, linkId) != -1)) {
        continue;
      }
      var parentId = link.getData().getStartId();
      var childId = link.getData().getEndId();
      if (this._containerLinkPromotionMap && this._containerPromotionLinkMap) {
        var promotion = this._containerLinkPromotionMap[linkId];
        if (promotion) {
          parentId = promotion[0];
          childId = promotion[1];
        }
      }
      if (nodeIds[parentId] && nodeIds[childId]) {
        var lc = this.CreateLayoutContextLink(link, parentId, childId, layout, layoutContext);
        layoutContext.addLink(lc);
      }
    }

    this.AddCrossContainerLinksToLayoutContext(layoutContext, groupNode, animator);

    if (layoutContext.getNodeCount() > 0) {
      this.SortLayoutContext(layoutContext);
      layoutContext = this.getLayoutCallback().doLayout(this, layoutContext);
      if (layoutContext) {
        this.ApplyLayoutContext(layoutContext, animator, bSaveOffset);
      }
    }
  }
};

/**
 * @protected
 * Add links across containers
 * @param {DvtDiagramLayoutContext} layoutContext  diagram layout context
 * @param {dvt.AdfDiagramNode} groupNode  diagram group node
 * @param {dvt.Animator} animator The animator to animate the changes into
 */
dvt.AdfDiagram.prototype.AddCrossContainerLinksToLayoutContext = function(layoutContext, groupNode, animator)
{
  var arCrossContainerLinkIds = null;
  var groupId = null;
  var layout = layoutContext.getLayout();
  if (!groupNode)
  {
    arCrossContainerLinkIds = this._arCrossContainerLinkIds;
  }
  else
  {
    arCrossContainerLinkIds = groupNode.getCrossChildLinkIds();
    groupId = groupNode.getData().getId();
  }

  if (arCrossContainerLinkIds)
  {
    for (var i = 0; i < arCrossContainerLinkIds.length; i++)
    {
      var linkId = arCrossContainerLinkIds[i];
      var link = this.getLinkById(linkId);
      if (!link.getVisible())
      {
        continue;
      }
      var parentId = link.getData().getStartId();
      var childId = link.getData().getEndId();
      if (this._containerLinkPromotionMap && this._containerPromotionLinkMap) {
        var promotion = this._containerLinkPromotionMap[linkId];
        if (promotion) {
          parentId = promotion[0];
          childId = promotion[1];
        }
      }

      var lc = this.CreateLayoutContextLink(link, parentId, childId, layout, layoutContext);
      layoutContext.addLink(lc);

      var linkParentNode = this.getNodeById(parentId);
      var linkChildNode = this.getNodeById(childId);
      var pNodeContainers = this.GetContainerIdArray(linkParentNode);
      var cNodeContainers = this.GetContainerIdArray(linkChildNode);
      this._addCrossContainerNodes(layoutContext, groupId, pNodeContainers, animator);
      this._addCrossContainerNodes(layoutContext, groupId, cNodeContainers, animator);
    }
  }
};

/**
 * Sorts the layout nodes and links to ensure consistent ordering across rendering formats
 * @param {DvtDiagramLayoutContext} layoutContext
 * @protected
 */
dvt.AdfDiagram.prototype.SortLayoutContext = function(layoutContext) {
  var diagram = this;
  var comparator = function(a, b) {
    var aIndex = a.getData().getRowIndex();
    var bIndex = b.getData().getRowIndex();
    return aIndex == bIndex ? 0 : (aIndex > bIndex ? 1 : -1);
  };
  var nodeComparator = function(a, b) {
    return comparator(diagram.getNodeById(a.getId()), diagram.getNodeById(b.getId()));
  };
  var linkComparator = function(a, b) {
    return comparator(diagram.getLinkById(a.getId()), diagram.getLinkById(b.getId()));
  };
  layoutContext._arNodes.sort(nodeComparator);
  layoutContext._arLinks.sort(linkComparator);
};

/**
 * Get the layout applied to the group node
 * @param {dvt.AdfDiagramNode} node  diagram group node
 * @return {string} layout
 */
dvt.AdfDiagram.prototype.GetLayoutAppliedToNode = function(node)
{
  var layout = this.getLayout();
  if (node)
  {
    var groupId = node.getData().getGroupId();
    if (groupId)
    {
      var groupNode = this.getNodeById(groupId);
      var groupLayout = groupNode.getLayout();
      if (groupLayout) {
        layout = groupLayout;
      }
    }
  }
  return layout;
};


/**
 * @protected
 * Creates layout context
 * @param {string} layout The name of the applied layout
 * @param {object} layoutAttrs map of global layout attributes
 * @param {string} groupId THe id for the container node when layout applied to the nodes inside of a container
 * @return {DvtDiagramLayoutContext} layout context
 */
dvt.AdfDiagram.prototype.CreateLayoutContext = function(layout, layoutAttrs, groupId)
{
  if (!layout)
  {
    layout = this.getLayout();
  }
  if (!layoutAttrs)
  {
    layoutAttrs = this.getLayoutAttributes(layout);
  }

  var layoutContext = this.CreateEmptyLayoutContext();
  layoutContext.setLayout(layout);
  layoutContext.setLayoutAttributes(layoutAttrs);
  if (groupId) {
    layoutContext.setContainerId(groupId);

    //Bug 14823204: container padding for the container whose children are being laid out
    var groupNode = this.getNodeById(groupId);
    if (groupNode) {
      var containerPadding = groupNode.getContainerPadding();
      if (containerPadding) {
        layoutContext.setContainerPadding(dvt.BaseDiagram.getLayoutContainerPadding(containerPadding));
      }
    }
  }

  // pass the current viewport to the layout if the component is already rendered and nodes/links have to be repositioned (e.g. container open/close operations)
  // or nodes/links have to be repositioned after PPR (e.g. node added/removed)
  if (this._bRendered || this._currentViewport) {
    var viewportRect = this._currentViewport ? this._currentViewport : this.getPanZoomCanvas().getViewport();
    var point = this._layoutOffset ? this._layoutOffset : new dvt.Point(0, 0);
    var currentViewport = new dvt.DiagramRectangle(viewportRect.x - point.x, viewportRect.y - point.y, viewportRect.w, viewportRect.h);
    layoutContext.setCurrentViewport(currentViewport);
  }

  return layoutContext;
};

dvt.AdfDiagram.prototype._resetTransitionState = function() {
  this._nodesPostFilter = null;
  this._linksPostFilter = null;
  this._nodesPostDrill = null;
  this._linksPostDrill = null;
  this._nodesPostIsolate = null;
  this._linksPostIsolate = null;
  this._nodesPostStack = null;
  this._linksPostStack = null;
};

dvt.AdfDiagram.prototype.GetXmlDomParser = function()
{
  return new dvt.AdfDiagramParser(this);
};

/**
 * Get Json parser
 * @protected
 */
dvt.AdfDiagram.prototype.GetJsonParser = function()
{
  return new dvt.AdfDiagramJsonParser(this.GetXmlDomParser(), this);
};

dvt.AdfDiagram.prototype.CreateNode = function(nodeData) {
  return new dvt.AdfDiagramNode(this.getCtx(), nodeData, this);
};

dvt.AdfDiagram.prototype.CreateLink = function(linkData) {
  return new dvt.AdfDiagramLink(this.getCtx(), linkData, this);
};

/**
 * Add node data
 * @param {dvt.AdfDiagramNodeData} nodeData  node data
 */
dvt.AdfDiagram.prototype.addNodeData = function(nodeData)
{
  //ignore duplicate data
  var nodeId = nodeData.getId();
  var node = this.getNodeById(nodeId);
  if (node) {
    if (node.getData().isStructureOnly()) {
      node._data = nodeData;
    }
    else {
      return;
    }
  }
  else {
    node = this.CreateNode(nodeData);
    this._nodes[nodeId] = node;
    this._nodesByRowIndex[nodeData.getRowIndex()] = node;
    this._arNodeIds.push(nodeId);
  }

  if (!nodeData.isStructureOnly()) {
    node.setNodeDef(this.GetNodeDef(nodeData.getTemplateId()));

    var groupId = nodeData.getGroupId();
    if (groupId)
    {
      this.getNodeById(groupId).addChildNode(node);
    }
    else
    {
      //this.getPanZoomCanvas().getContentPane().addChild(node);
      this._nodesPane.addChild(node);
      this._rootNodes.push(nodeId);
    }

    if (this._bRendered) {
      //Bug 14187300: in editor mode, _newNodes doesn't exist
      if (this._newNodes) {
        this._newNodes.push(node);
      }
    }

    var nodeAttrGrps = nodeData.getAttributeGroups();
    if (nodeAttrGrps)
    {
      if (!this._attrGrpsMap)
      {
        this._attrGrpsMap = {};
      }
      var numNodeAttrGrps = nodeAttrGrps.length;
      for (var i = 0; i < numNodeAttrGrps; i++)
      {
        var nodeAttrGrp = nodeAttrGrps[i];
        if (nodeAttrGrp)
        {
          var storedVal = this._attrGrpsMap[nodeAttrGrp];
          if (!storedVal)
          {
            storedVal = [];
            this._attrGrpsMap[nodeAttrGrp] = storedVal;
          }
          storedVal.push(nodeId);
        }
      }
    }

    //Bug 14803334 - HTML5: PANELCARD DIDN'T STAMP PROPERLY AFTER PPR
    // apply panelCard index if needed
    var cardIndexes = this._cardIndexes;
    if (cardIndexes) {

      //find current node's index and syncAll index in cardIndexes array
      var synAllIndex = -1;
      var index = -1;

      if (cardIndexes.length > 0) {
        var key = dvt.AfPanelCard.DISPLAYED_DETAIL_ITEM_INDEX_KEY;
        for (var inx = 0; inx < cardIndexes.length; inx++) {
          var entry = cardIndexes[inx];
          if (entry && entry.length == 2) {
            if (entry[0] == dvt.AdfDiagram.SYNC_PANEL_CARDS_KEY) {
              synAllIndex = inx;
              var syncIdx = this._findPanelCardIndex(node, entry[1]);
              node.setStateProp(key, syncIdx);
            }
            else if (entry[0] == nodeId) {
              index = inx;
              node.setStateProp(key, entry[1]);
            }
          }
        }
      }
      // remove the old entry if it is overridden by sync all panelCards
      if (synAllIndex > index && index != -1) {
        cardIndexes.splice(index, 1);
      }
    }
  }

};

/**
 * Add link data
 * @param {DvtAdfDiagramLinkData} linkData  link data
 */
dvt.AdfDiagram.prototype.addLinkData = function(linkData)
{
  //ignore duplicate data
  if (this.getLinkById(linkData.getId()))
    return;

  var link = this.CreateLink(linkData);
  link.setLinkDef(this.GetLinkDef(linkData.getTemplateId()));
  this._links[linkData.getId()] = link;
  this._linksByRowIndex[linkData.getRowIndex()] = link;
  this._arLinkIds.push(linkData.getId());

  var pNode = this.getNodeById(linkData.getStartId());
  var cNode = this.getNodeById(linkData.getEndId());
  if (!pNode || !cNode)
    return;
  pNode.addOutLinkId(linkData.getId());
  cNode.addInLinkId(linkData.getId());

  var groupId = linkData.getGroupId();
  var pGroupId = pNode.getData().getGroupId();
  var cGroupId = cNode.getData().getGroupId();

  if (groupId)
  {
    if (groupId == pGroupId && groupId == cGroupId) {
      this.getNodeById(groupId).addChildLink(link);
    }
    else {
      this.getNodeById(groupId).addCrossChildLink(link);
    }
  }
  else
  {
    if (pGroupId || cGroupId) {
      //add to nodesPane because cross-container links must draw
      //on top of nodes
      this._nodesPane.addChild(link);
      link._linkParent = this._nodesPane;
      link._bCrossContainerLink = true;

      //remember that this is a cross-container link
      if (!this._arCrossContainerLinkIds)
      {
        this._arCrossContainerLinkIds = [];
      }
      this._arCrossContainerLinkIds.push(linkData.getId());
    }
    else
    {
      //put links underneath nodes in z-order
      //this.getPanZoomCanvas().getContentPane().addChildAt(link, 0);
      this._linksPane.addChild(link);
      link._linkParent = this._linksPane;
      link._bCrossContainerLink = false;
    }
  }
};

/**
 * @protected
 * Get container ids
 * @param {dvt.AdfDiagramNode} node diagram node
 * @return {array} array of container ids
 */
dvt.AdfDiagram.prototype.GetContainerIdArray = function(node)
{
  var arContainerIds = [node.getData().getId()];
  var groupId = node.getData().getGroupId();
  while (groupId)
  {
    if (!arContainerIds)
    {
      arContainerIds = [];
    }
    arContainerIds.push(groupId);

    var groupNode = this.getNodeById(groupId);
    groupId = groupNode.getData().getGroupId();
  }

  //reverse array
  if (arContainerIds)
  {
    arContainerIds.reverse();
  }

  return arContainerIds;
};

/**
 * Get node by id
 * @param {string} id  node id
 * @return {dvt.AdfDiagramNode} diagram node
 */
dvt.AdfDiagram.prototype.getNodeById = function(id)
{
  return this._nodes[id];
};

/**
 * Get the Diagram Node by row index
 * @param {Number} index row index
 * @return {dvt.AdfDiagramNode} Diagram Node object
 */
dvt.AdfDiagram.prototype.GetNodeByRowIndex = function(index)
{
  return this._nodesByRowIndex[index];
};

/**
 * Get link by id
 * @param {string} id  link id
 * @return {dvt.AdfDiagramLink} diagram link
 */
dvt.AdfDiagram.prototype.getLinkById = function(id)
{
  return this._links[id];
};

/**
 * Get the Diagram Link by row index
 * @param {Number} index row index
 * @return {dvt.AdfDiagramLink}  Diagram Link object
 */
dvt.AdfDiagram.prototype.GetLinkByRowIndex = function(index)
{
  return this._linksByRowIndex[index];
};

dvt.AdfDiagram.prototype.addNodeDef = function(nodeDef)
{
  this._nodeDefs[nodeDef.getId()] = nodeDef;
};

dvt.AdfDiagram.prototype.addLinkDef = function(linkDef)
{
  this._linkDefs[linkDef.getId()] = linkDef;
};

dvt.AdfDiagram.prototype.GetNodeDef = function(id)
{
  return this._nodeDefs[id];
};

dvt.AdfDiagram.prototype.GetLinkDef = function(id)
{
  return this._linkDefs[id];
};

//Performance: parse, stamp af components and connect them to the parent
//but not sizing or layout here
dvt.AdfDiagram.prototype.CreateNodeContentsAtDepth = function(arNodes, afContext, animator, bZoom100)
{
  for (var i = 0; i < arNodes.length; i++)
  {
    var node = arNodes[i];
    if (bZoom100) {
      //re-render zoom100 when images loaded
      if (this._renderZoom100Nodes) {
        if (this._renderZoom100Nodes[node.getData().getId()]) {
          // save zoom flag
          var oZoomFlag = node.getForceZoom100Facet();
          node.setForceZoom100Facet(true);

          //Bug 16953938 - SIZE OF THE DIAGRAM NODE UNDERLAY IS WRONG
          if (node.isDisclosed()) {
            node.setDisclosed(false);
            node.createNodeContents(afContext, animator);
            node.setDisclosed(true);
          }
          else {
            node.createNodeContents(afContext, animator);
          }
          // restore zoom flag
          node.setForceZoom100Facet(oZoomFlag);
        }
      }
      else {
        node.createNodeContents(afContext, animator);
      }
    }
    else {
      node.createNodeContents(afContext, animator);
    }
  }

};

//Bug 15838158: store the id of the node whose stack is being previewed so that we can handle a
//component resize properly
dvt.AdfDiagram.prototype.SetStackPreviewNodeId = function(nodeId) {
  this._stackPreviewNodeId = nodeId;
};

dvt.AdfDiagram.prototype.GetStackPreviewNodeId = function() {
  return this._stackPreviewNodeId;
};

dvt.AdfDiagram.prototype.IsStackPreview = function() {
  return (this._stackPreviewNodeId ? true : false);
};


/**
 * Processes events from this component's dvt.PanelDrawer
 * @param {dvt.PanelDrawerEvent} event a panel drawer event to process
 * @protected
 */
dvt.AdfDiagram.prototype.HandlePanelDrawerEvent = function(event) {
  var panelId = event.getActivePanel();
  var isDisclosed = event.getSubType() == dvt.PanelDrawerEvent.SUBTYPE_SHOW;
  var propEvent = dvt.EventFactory.newAdfPropertyChangeEvent();

  if (panelId == dvt.PanelDrawer.PANEL_OVERVIEW && this._overviewWindowPanel != this._panelDrawer) {
    propEvent['properties'][dvt.OverviewWindow.OVERVIEW_DISCLOSED_KEY] = isDisclosed;
  } else {
    // if a subcomponent is not active, consider it not disclosed
    if (this._legend) {
      propEvent['properties'][dvt.PanZoomComponent.LEGEND_DISCLOSED_EVENT_KEY] = panelId == dvt.PanelDrawer.PANEL_LEGEND && isDisclosed;
    }
    if (this._palette) {
      propEvent['properties'][dvt.Palette.PALETTE_DISCLOSED_KEY] = panelId == dvt.PanelDrawer.PANEL_PALETTE && isDisclosed;
    }
    if (this._overviewWindow && this._overviewWindowPanel == this._panelDrawer) {
      propEvent['properties'][dvt.OverviewWindow.OVERVIEW_DISCLOSED_KEY] = panelId == dvt.PanelDrawer.PANEL_OVERVIEW && isDisclosed;
    }
    if (this._searchPanel) {
      var discloseSearch = (panelId == dvt.PanelDrawer.PANEL_SEARCH && isDisclosed);
      var searchEvent = new dvt.SearchEvent(discloseSearch ? dvt.SearchEvent.SEARCH_EXPAND_EVENT : dvt.SearchEvent.SEARCH_COLLAPSE_EVENT);
      this.dispatchEvent(searchEvent);
      // Bug 16787467 - HTML5 HV: JS ERROR AFTER CLOSE SEARCHPANEL
      return;
    }
  }
  this.dispatchEvent(propEvent);

  //update context keyboard handlers
  this.setContextKeyboardHandlers();
};

/**
 * @override
 */
dvt.AdfDiagram.prototype.HandlePanEvent = function(event) 
{
  var subType = event.getSubType();

  dvt.AdfDiagram.superclass.HandlePanEvent.call(this, event);

  if (subType === dvt.PanEvent.SUBTYPE_PANNED) {
    if (this.IsStackPreview()) {
      this._updatePreviewStackPanZoomState(false);
      return;
    } else {
      this._updatePanZoomState(false);
    }
  }

  //hide the transient toolbar on a pan
  this.HideNodeTransientToolbar(true);
  //ENH #7281410: hide interactive hover window when panning
  this.HideNodeHoverDetails();

  if (this._legendPanel)
  {
    if (subType === dvt.PanEvent.SUBTYPE_DRAG_PAN_BEGIN)
    {
      this._legendPanel.setMouseEnabled(false);
    }
    else if (subType === dvt.PanEvent.SUBTYPE_DRAG_PAN_END)
    {
      this._legendPanel.setMouseEnabled(true);
    }
  }
  if (this._palettePanel)
  {
    if (subType === dvt.PanEvent.SUBTYPE_DRAG_PAN_BEGIN)
    {
      this._palettePanel.setMouseEnabled(false);
    }
    else if (subType === dvt.PanEvent.SUBTYPE_DRAG_PAN_END)
    {
      this._palettePanel.setMouseEnabled(true);
    }
  }
  if (this._overviewWindowPanel)
  {
    if (subType === dvt.PanEvent.SUBTYPE_PANNING)
    {
      DvtAdfDiagramOverviewUtils.HandlePanEvent(this, event);
    }
    else if (subType === dvt.PanEvent.SUBTYPE_DRAG_PAN_BEGIN)
    {
      this._overviewWindowPanel.setMouseEnabled(false);
    }
    else if (subType === dvt.PanEvent.SUBTYPE_DRAG_PAN_END)
    {
      this._overviewWindowPanel.setMouseEnabled(true);
    }
  }
  if (this._panelDrawer) {
    if (subType === dvt.PanEvent.SUBTYPE_DRAG_PAN_BEGIN) {
      this._panelDrawer.deEmphasizeStart();
    }
    else if (subType === dvt.PanEvent.SUBTYPE_DRAG_PAN_END) {
      this._panelDrawer.deEmphasizeEnd();
    }
  }
  else if (this.getSearchPanel()) {
    if (subType === dvt.PanEvent.SUBTYPE_DRAG_PAN_BEGIN)
    {
      this.getSearchPanel().deEmphasizeStart();
    }
    else if (subType === dvt.PanEvent.SUBTYPE_DRAG_PAN_END)
    {
      this.getSearchPanel().deEmphasizeEnd();
    }
  }
};


/**
 * @override
 */
dvt.AdfDiagram.prototype.HandleZoomEvent = function(event)
{
  var subType = event.getSubType();

  dvt.AdfDiagram.superclass.HandleZoomEvent.call(this, event);

  if (this.IsStackPreview()) {
    this._updatePreviewStackPanZoomState(subType == dvt.ZoomEvent.SUBTYPE_ZOOM_TO_FIT_END);
    return;
  }

  switch (subType)
  {
    case dvt.ZoomEvent.SUBTYPE_ADJUST_PAN_CONSTRAINTS:
      if (this.IsPanningEnabled()) {
        var zoom = event.getNewZoom();
        // Calculate the new content dimensions based on the new zoom
        var contentDim = this._cachedViewBounds ? this._cachedViewBounds : this.GetViewBounds(event.getAnimator());
        this.ConstrainPanning(contentDim.x, contentDim.y, contentDim.w, contentDim.h, zoom);
      }
      break;
    case dvt.ZoomEvent.SUBTYPE_ZOOMING:
      // hide node flyouts when zoom begins
      this.HideNodeFlyOuts(true);
      //ENH #7281410: hide interactive hover window when zooming
      this.HideNodeHoverDetails();
      break;
    case dvt.ZoomEvent.SUBTYPE_ZOOM_TO_FIT_CALC_BOUNDS:
      //do not override a viewport returned from the layout engine
      if (!this.IsLayoutViewport()) {
        // Calculate the new content dimensions based on the new zoom
        var contentDim = this._cachedViewBounds ? this._cachedViewBounds : this.GetViewBounds(event.getAnimator());
        event.setZoomToFitBounds(contentDim);
      }
      break;
    case dvt.ZoomEvent.SUBTYPE_ZOOM_TO_FIT_BEGIN:
      this._setRerenderImmediate(true);
      break;
    case dvt.ZoomEvent.SUBTYPE_ZOOM_TO_FIT_END:
      this._updatePanZoomState(true);
      this._setRerenderImmediate(false);
      break;
    case dvt.ZoomEvent.SUBTYPE_ZOOM_AND_CENTER:
      this.HandleZoomAndCenter(event);
      break;
    case dvt.ZoomEvent.SUBTYPE_ZOOMED:
      if (event.getOldZoom() !== event.getNewZoom())
      {
        if (!this._zoomBeforeRender) {
          // Store the original zoom so that when the timer below goes off, we can tell whether we need to fully
          // rerender or just refresh the stroke widths
          this._zoomBeforeRender = event.getOldZoom();
        }
        var context = this.getCtx();
        //Bug 16940001: rerender immediately if this is the initial display
        if (!dvt.Agent.isEnvironmentBrowser() || !this._bRendered || this._getRerenderImmediate()) {
          this.Rerender();
        }
        else {
          if (this.RerenderTimer) {
            this.RerenderTimer.reset();
          }
          else {
            this.RerenderTimer = new dvt.Timer(context, this.RerenderWaitTime, this.Rerender, this, 1);
          }
          this.RerenderTimer.start();
        }
      }

      //refresh the transient toolbar so that it scales to 100%
      var ttDataId = this.GetTransientToolbarDataId();
      if (ttDataId) {
        var ttDataObj = this.getNodeById(ttDataId);
        if (ttDataObj && ttDataObj.IsTransientToolbarOnClick()) {
          ttDataObj.RefreshTransientToolbar();
        }
      }

      //hide the container badge when the view is zoomed out
      for (var nodeId in this._nodes) {
        var node = this.getNodeById(nodeId);
        if (node.getVisible()) {
          node.RefreshContainerBadgeVisibility();
        }
      }
      this._updatePanZoomState(false);
      break;
    case dvt.ZoomEvent.SUBTYPE_ZOOM_END:
      for (var nodeId in this._nodes) {
        var node = this.getNodeById(nodeId);
        node.cleanUp();
      }
      break;
  }

  if (this._legendPanel)
  {
    if (subType === dvt.ZoomEvent.SUBTYPE_DRAG_ZOOM_BEGIN)
    {
      this._oldLegendAlpha = this._legendPanel.getAlpha();
      this._legendPanel.setAlpha(dvt.AdfDiagram._MOUSE_DRAG_ALPHA);
      this._legendPanel.setMouseEnabled(false);
    }
    else if (subType === dvt.ZoomEvent.SUBTYPE_DRAG_ZOOM_END)
    {
      this._legendPanel.setAlpha(this._oldLegendAlpha);
      this._legendPanel.setMouseEnabled(true);
    }
  }

  if (this._overviewWindowPanel)
  {
    if (subType === dvt.ZoomEvent.SUBTYPE_ZOOMING) {
      DvtAdfDiagramOverviewUtils.HandleZoomEvent(this, event);
    }
    else if (subType === dvt.ZoomEvent.SUBTYPE_DRAG_ZOOM_BEGIN)
    {
      this._oldOverviewAlpha = this._overviewWindowPanel.getAlpha();
      this._overviewWindowPanel.setAlpha(dvt.AdfDiagram._MOUSE_DRAG_ALPHA);
      this._overviewWindowPanel.setMouseEnabled(false);
    }
    else if (subType === dvt.ZoomEvent.SUBTYPE_DRAG_ZOOM_END)
    {
      this._overviewWindowPanel.setAlpha(this._oldOverviewAlpha);
      this._overviewWindowPanel.setMouseEnabled(true);
    }
  }
};


/**
 * Rerenders if necessary in response to a zoom event.
 */
dvt.AdfDiagram.prototype.Rerender = function() {
  var currentZoom = this.getPanZoomCanvas().getZoom();
  if (this._zoomBeforeRender != currentZoom) {
    var oldZoom = Math.min(this._zoomBeforeRender, 1);
    var newZoom = Math.min(currentZoom, 1);
    oldZoom = Math.ceil(oldZoom / .25);
    newZoom = Math.ceil(newZoom / .25);
    if (oldZoom !== newZoom) {
      //maintain flag when crossing zoom threshold so the nodes
      //don't have to render if the template didn't change
      this._bCrossedZoomThreshold = true;
      this.RenderComponentInternal();
      this._bCrossedZoomThreshold = false;
    }
    else if (!this.supportsVectorEffects()) { // didn't cross zoom threshold, but need to manually scale the strokes
      // Refresh the selection effect on all visible nodes
      var nodesByGroupDepth = this.getSortedVisibleNodesByGroupDepth();
      for (var depth = nodesByGroupDepth.length - 1; depth >= 0; depth--) {
        var arNodes = nodesByGroupDepth[depth];
        for (var i = 0; i < arNodes.length; i++) {
          arNodes[i].RefreshSelectionEffect();
        }
      }
    }
  }
  this._zoomBeforeRender = null;
};

/**
 * Zoom and center the selected nodes/links
 * @param {DvtEvent} event  Dvt event
 */
dvt.AdfDiagram.prototype.HandleZoomAndCenter = function(event)
{
  var arSelectedNodes = [];
  var arSelectedLinks = [];
  var node;
  var link;
  if (!this._selectionHandler) {
    return;
  }
  var selection = this._selectionHandler.getSelection();
  if (selection) {
    for (var selI = 0; selI < selection.length; selI++) {
      var selItem = selection[selI];
      if (selItem instanceof dvt.AdfDiagramNode) {
        if (selItem.getVisible() && !selItem.isHiddenAncestor()) {
          arSelectedNodes.push(selItem);
        }
      }
      else if (selItem instanceof dvt.AdfDiagramLink) {
        if (selItem.getVisible()) {
          arSelectedLinks.push(selItem);
        }
      }
    }
  }

  if (arSelectedNodes.length > 0 || arSelectedLinks.length > 0) {
    var bounds;
    var i;
    var nodeBounds;
    var linkBounds;
    for (i = 0; i < arSelectedNodes.length; i++) {
      node = arSelectedNodes[i];
      nodeBounds = node.getDimensions(this.getPanZoomCanvas().getContentPane());
      if (!bounds) {
        bounds = new dvt.Rectangle(nodeBounds.x, nodeBounds.y, nodeBounds.w, nodeBounds.h);
      }
      else {
        bounds.grow(nodeBounds.x, nodeBounds.y);
        bounds.grow(nodeBounds.x + nodeBounds.w, nodeBounds.y + nodeBounds.h);
      }
    }
    for (i = 0; i < arSelectedLinks.length; i++) {
      link = arSelectedLinks[i];
      linkBounds = link.getDimensions(this.getPanZoomCanvas().getContentPane());
      if (!bounds) {
        bounds = new dvt.Rectangle(linkBounds.x, linkBounds.y, linkBounds.w, linkBounds.h);
      }
      else {
        bounds.grow(linkBounds.x, linkBounds.y);
        bounds.grow(linkBounds.x + linkBounds.w, linkBounds.y + linkBounds.h);
      }
    }
  }

  var animator = new dvt.Animator(this.getCtx(), this.getAnimationDuration());
  this.getPanZoomCanvas().center(animator, bounds);
  this.getPanZoomCanvas().zoomTo(1.0, null, null, animator);
  animator.play();
};


dvt.AdfDiagram.prototype.IsInView = function(bounds)
{
  var pzc = this.getPanZoomCanvas();
  var panX = pzc.getPanX();
  var panY = pzc.getPanY();
  var zoom = pzc.getZoom();

  var bb = new dvt.Rectangle(bounds.x, bounds.y, bounds.w, bounds.h);
  bb.x = bb.x + panX;
  bb.y = bb.y + panY;
  bb.w = bb.w * zoom;
  bb.h = bb.h * zoom;

  return (bb.x >= 0 && bb.x <= this.Width &&
          bb.y >= 0 && bb.y <= this.Height &&
          bb.x + bb.w >= 0 && bb.x + bb.w <= this.Width &&
          bb.y + bb.h >= 0 && bb.y + bb.h <= this.Height);
};


/**
 * Sets attribute groups and exploded stacks. Dispatches an event and animate transition for the stacked/unstacked nodes if necessary.
 * @param {array} arAttrGrps attribute groups array
 * @param {array} arExplodedStacks exploded stacks array
 * @protected
 */
dvt.AdfDiagram.prototype.SetAttributeGroupsHelper = function(arAttrGrps, arExplodedStacks)
{
  this._arOldAttrGrps = this._arAttrGrps;
  this._arOldExplodedStacks = this._arExplodedStacks;

  this._arAttrGrps = arAttrGrps;
  this._arExplodedStacks = arExplodedStacks;

  if (this._bRendered && !this._eventType)
  {
    var event = dvt.EventFactory.newAdfPropertyChangeEvent();
    event['properties'][dvt.AdfDiagram.GROUP_BY_KEY] = arAttrGrps ? arAttrGrps.join(dvt.AdfDiagram.ROWKEY_DELIMITER) : '';
    event['properties'][dvt.AdfDiagram.GROUP_BY_UNGROUPED_KEY] = arExplodedStacks ? arExplodedStacks.join(dvt.AdfDiagram.ROWKEY_DELIMITER) : '';
    this.dispatchEvent(event);

    dvt.AdfDiagramTransitionUtils.processStack(this);
  }
  this._arOldAttrGrps = null;
  this._arOldExplodedStacks = null;
};

dvt.AdfDiagram.prototype.setAttributeGroups = function(arAttrGrps)
{
  this.SetAttributeGroupsHelper(arAttrGrps, null);
};

dvt.AdfDiagram.prototype.getAttributeGroups = function()
{
  return this._arAttrGrps;
};

dvt.AdfDiagram.prototype.setExplodedStacks = function(arStackNames)
{
  this.SetAttributeGroupsHelper(this.getAttributeGroups(), arStackNames);
};

dvt.AdfDiagram.prototype.getExplodedStacks = function()
{
  return this._arExplodedStacks;
};

/**
 * Sets hidden attribute groups. Dispatches an event and and animate transition for the hidden nodes if necessary.
 * @param {array} arHiddenAttrGrps hidden attribute groups array
 * @protected
 */
dvt.AdfDiagram.prototype.setHiddenAttributeGroups = function(arHiddenAttrGrps)
{
  this._arHiddenAttrGrps = arHiddenAttrGrps;

  if (this._bRendered && !this._eventType)
  {
    var event = dvt.EventFactory.newAdfPropertyChangeEvent(dvt.AdfDiagram.GROUP_BY_HIDDEN_KEY, arHiddenAttrGrps ? arHiddenAttrGrps.join(dvt.AdfDiagram.ROWKEY_DELIMITER) : '');
    this.dispatchEvent(event);
    dvt.AdfDiagramTransitionUtils.processFilter(this);
  }
};

dvt.AdfDiagram.prototype.getHiddenAttributeGroups = function()
{
  return this._arHiddenAttrGrps;
};


/**
 * Sets the overview window for this component
 *
 * @param {dvt.OverviewWindow} ovWindow the overview window for this component
 */
dvt.AdfDiagram.prototype.setOverviewWindow = function(ovWindow) {
  if (this._overviewWindow) {
    this._overviewWindow.removeEvtListener(dvt.ViewportChangeEvent.TYPE, this.HandleViewportChange, false, this);
  }
  this._overviewWindow = ovWindow;
  this._overviewWindow.addEvtListener(dvt.ViewportChangeEvent.TYPE, this.HandleViewportChange, false, this);
};

dvt.AdfDiagram.prototype.getOverviewWindow = function()
{
  return this._overviewWindow;
};


/**
 * Returns the overview window panel, either a dvt.CollapsiblePanel or a dvt.PanelDrawer.
 */
dvt.AdfDiagram.prototype.__getOverviewWindowPanel = function()
{
  return this._overviewWindowPanel;
};


/**
 * Handler for ViewportChangeEvents from the overview window
 *
 * @param {dvt.ViewportChangeEvent} event the event from the overview window
 */
dvt.AdfDiagram.prototype.HandleViewportChange = function(event) {
  var newDim = event.getNewDimensions();
  var oldDim = event.getOldDimensions();

  var zoom = this.getPanZoomCanvas().getZoom();
  var dx = ((newDim.x - oldDim.x) * zoom);
  var dy = ((newDim.y - oldDim.y) * zoom);

  this.getPanZoomCanvas().panBy(-dx, -dy);
};

dvt.AdfDiagram.prototype.UpdateOverviewNode = function(node, animator) 
{
  //Bug 15838442: only update overview node if this is not a stack preview
  if (this._overviewWindow && !this.IsStackPreview())
  {
    DvtAdfDiagramOverviewUtils.UpdateOverviewNode(this, node, animator, this.getOverviewWindowContentStyle());
  }
};

/**
 * @protected
 * Call the function for every node
 * @param {function} func function to call
 * @param {object} funcObj function param object
 */
dvt.AdfDiagram.prototype.VisitNodes = function(func, funcObj) 
{
  for (var nodeId in this._nodes)
  {
    var node = this.getNodeById(nodeId);
    func.call(funcObj, node);
  }
};

/**
 * @protected
 * Call the function for every link
 * @param {function} func function to call
 * @param {object} funcObj function param object
 */
dvt.AdfDiagram.prototype.VisitLinks = function(func, funcObj) 
{
  for (var linkId in this._links)
  {
    var link = this.getLinkById(linkId);
    func.call(funcObj, link);
  }
};

/**
 * Sets the disclosure state for a node
 *
 * @param {string} nodeId the id of the node
 * @param {boolean} bDisclosed whether the node should be disclosed or not
 * @param {boolean} suppressAnimation whether animation should suppressed
 * @param {boolean} bFetch whether this node disclosure originates from the processing of a fetch event
 */
dvt.AdfDiagram.prototype.setNodeDisclosed = function(nodeId, bDisclosed, suppressAnimation, bFetch) {
  if (bDisclosed) {
    var node = this.getNodeById(nodeId);
    if (!node.getData().isChildrenFetched()) {
      // Send a fetch event to the server
      this.setInteractionEnabled(false);
      this.dispatchEvent(new dvt.AdfDiagramFetchEvent(dvt.AdfDiagramFetchEvent.DISCLOSE, nodeId));
      return;
    }
  }
  var bDisclosureChanged = false;
  var index = -1;
  if (this._disclosedNodes) {
    index = dvt.ArrayUtils.getIndex(this._disclosedNodes, nodeId);
  }
  if (!bDisclosed && index > -1) {
    this._disclosedNodes.splice(index, 1);
    bDisclosureChanged = true;
  }
  else if (bDisclosed && index < 0) {
    if (!this._disclosedNodes) {
      this._disclosedNodes = [];
    }
    this._disclosedNodes.push(nodeId);
    bDisclosureChanged = true;
  }

  // In the case of a fetch event, the disclosure state already changed on the server and was pushed to the client
  // so even though it looks like the disclosure state didn't change, it really did
  if (bDisclosureChanged || bFetch) {
    //TODO: change this to false to let transition utils animate it
    var bVisOnly = false;
    //Bug 13393132: save the container being opened or closed
    if (bDisclosed) {
      this._disclosingNode = nodeId;
    }
    else {
      this._undisclosingNode = nodeId;
    }
    if (!suppressAnimation) {
      dvt.AdfDiagramTransitionUtils.processContainerDisclosure(this, bVisOnly);
    }

    //TODO: remove this?  Need to call this from here right now because
    //transition utils is not calling it when only visibility is applied
    if (bVisOnly) {
      var animator = new dvt.Animator(this.getCtx(), this.getAnimationDuration());
      this.RenderComponentInternal(animator);
      if (animator) {
        animator.play();
      }
    }
    if (bDisclosureChanged) {
      // Convert the row disclosure state to a string and store on the peer
      var event = dvt.EventFactory.newAdfPropertyChangeEvent(dvt.AdfDiagram.ROW_DISCLOSURE_KEY, this._disclosedNodes.join(dvt.AdfDiagram.ROWKEY_DELIMITER));
      this.dispatchEvent(event);
    }
  }
};

/**
 * Drills a diagram node
 * @param {string} nodeId the id of the node
 * @param {boolean} suppressAnimation whether animation should suppressed
 */
dvt.AdfDiagram.prototype.drillNode = function(nodeId, suppressAnimation) {
  var node = null;
  var allowDrill = true;
  if (nodeId) {
    node = this.getNodeById(nodeId);
    if (!node.isContainer()) {
      allowDrill = false;
    }
    else if (!node.getData().isChildrenFetched()) {
      // Send a fetch event to the server
      this.setInteractionEnabled(false);
      this.dispatchEvent(new dvt.AdfDiagramFetchEvent(dvt.AdfDiagramFetchEvent.DRILL, nodeId));
      return;
    }
  }
  if (allowDrill) {
    this._drilledNodePath = this._createDrillPath(nodeId);
    //Bug 15838130: move focus to child node of drilled node, if drilled
    //node currently has focus
    if (node && node.isFocused()) {
      var childNodeIds = node.getChildNodeIds();
      if (childNodeIds && childNodeIds.length > 0) {
        for (var childIndex = 0; childIndex < childNodeIds.length; childIndex++) {
          var childNode = this.getNodeById(childNodeIds[childIndex]);
          if (childNode.getVisible()) {
            this.EventManager.setFocusObj(childNode);
            break;
          }
        }
      }
    }
  }

  var bVisOnly = false;
  if (!suppressAnimation) {
    dvt.AdfDiagramTransitionUtils.processDrill(this, bVisOnly);
  }
  if (this._bRendered && allowDrill) {
    var event = dvt.EventFactory.newAdfPropertyChangeEvent(dvt.AdfDiagram.ROOT_ROW_KEY, nodeId ? nodeId : 'null');
    this.dispatchEvent(event);
  }

  //TODO: remove this?  Need to call this from here right now because
  //transition utils is not calling it when only visibility is applied
  if (bVisOnly) {
    var animator = new dvt.Animator(this.getCtx(), this.getAnimationDuration());
    this.RenderComponentInternal(animator);
    if (animator) {
      animator.play();
    }
  }
};

/**
 * @private
 * Create drill path for the node id
 * @param {string} nodeId  diagram node id
 * @return {array} drill path array
 */
dvt.AdfDiagram.prototype._createDrillPath = function(nodeId) {
  var drilledNodePath = null;
  if (nodeId) {
    drilledNodePath = [];
    var groupId = nodeId;
    while (groupId) {
      drilledNodePath.push(groupId);
      groupId = this.getNodeById(groupId).getData().getGroupId();
    }
    drilledNodePath.reverse();
  }
  return drilledNodePath;
};

dvt.AdfDiagram.prototype.isolateNode = function(nodeId, degrees, isolateType, suppressAnimation) {
  var nodeMap = this._getNodeNetwork(nodeId, degrees);
  this._isolateNodeMap(nodeMap, isolateType, suppressAnimation);
};

/**
 * @private
 * Get the node network
 * @param {string} nodeId  diagram node id
 * @param {number} degrees  degree of descendants
 * @return {object} the node network object
 */
dvt.AdfDiagram.prototype._getNodeNetwork = function(nodeId, degrees) {
  var descendants = this._getDescendants(nodeId, true);
  var network = this._mapUnion(null, descendants); // create copy of descendants
  if (degrees > 0) {
    for (var descId in descendants) {
      var node = this.getNodeById(descId);
      var outLinks = node.getOutLinkIds();
      if (outLinks) {
        for (var i = 0; i < outLinks.length; i++) {
          var link = this.getLinkById(outLinks[i]);
          network = this._mapUnion(network, this._getNodeNetwork(link.getData().getEndId(), degrees - 1));
        }
      }
      var inLinks = node.getInLinkIds();
      if (inLinks) {
        for (var i = 0; i < inLinks.length; i++) {
          var link = this.getLinkById(inLinks[i]);
          network = this._mapUnion(network, this._getNodeNetwork(link.getData().getStartId(), degrees - 1));
        }
      }
    }
  }
  return network;
};

/**
 * @private
 * Get descendants of the node
 * @param {string} nodeId  diagram node id
 * @param {boolean} bIncludeNode  true to include the container node
 * @return {object} descendants object
 */
dvt.AdfDiagram.prototype._getDescendants = function(nodeId, bIncludeNode) {
  var descendants = {};
  var node = this.getNodeById(nodeId);
  var needsFetch = node.isContainer() && !node.getData().isChildrenFetched();
  if (bIncludeNode) {
    descendants[nodeId] = needsFetch;
  }
  if (node.isContainer() && node.getData().isChildrenFetched()) {
    var childNodes = node.getChildNodeIds();
    for (var i = 0; i < childNodes.length; i++) {
      descendants = this._mapUnion(descendants, this._getDescendants(childNodes[i], true));
    }
  }
  return descendants;
};

/**
 * @private
 * Get ancestors of the given node
 * @param {string} nodeId  diagram node id
 * @param {boolean} bIncludeNode  true to include the container node
 * @return {object} ancestors object
 */
dvt.AdfDiagram.prototype._getAncestors = function(nodeId, bIncludeNode) {
  var ancestors = {};
  if (bIncludeNode) {
    var node = this.getNodeById(curId);
    var needsFetch = node.isContainer() && !node.getData().isChildrenFetched();
    ancestors[nodeId] = needsFetch;
  }
  var curId = nodeId;
  while (curId) {
    var node = this.getNodeById(curId);
    curId = node.getData().getGroupId();
    if (curId) {
      ancestors[curId] = false; // ancestors must have already been fetched since we're starting from a descendant
    }
  }
  return ancestors;
};

/**
 * Get the Diagram Node at given index
 * @param {Number} index node index
 * @return {dvt.AdfDiagramNode} Diagram Node object
 */
dvt.AdfDiagram.prototype.getNode = function(index)
{
  return this.GetNodeByRowIndex(index);
};

/**
 * Get the Diagram Link at given index
 * @param {Number} index link index
 * @return {dvt.AdfDiagramLink}  Diagram Link object
 */
dvt.AdfDiagram.prototype.getLink = function(index)
{
  return this.GetLinkByRowIndex(index);
};

/**
 * @override
 */
dvt.AdfDiagram.prototype.GetAllLinks = function() {
  var allLinks = [];
  for (var linkId in this._links) {
    allLinks.push(linkId);
  }
  return allLinks;
};
/**
 * @override
 */
dvt.AdfDiagram.prototype.GetAllNodes = function() {
  var allNodes = [];
  for (var nodeId in this._nodes) {
    allNodes.push(nodeId);
  }
  return allNodes;
};

/**
 * Get the map of all nodes
 * @return {object} map of all nodes
 * @private
 */
dvt.AdfDiagram.prototype._getAllNodeMap = function() {
  var allNodes = {};
  for (var nodeId in this._nodes) {
    var node = this.getNodeById(nodeId);
    var needsFetch = node.isContainer() && !node.isChildrenFetched();
    allNodes[nodeId] = needsFetch;
  }
  return allNodes;
};

dvt.AdfDiagram.prototype._getAllNodeObjects = function() {
  var allNodes = [];
  for (var nodeId in this._nodes) {
    allNodes.push(this._nodes[nodeId]);
  }
  return allNodes;
};

/**
 * Isolate link
 * @param {string} linkId  link id
 * @param {number} degrees  degree of descendants
 * @param {string} isolateType isolate action type
 * @param {boolean} suppressAnimation whether animation should be suppressed or not
 */
dvt.AdfDiagram.prototype.isolateLink = function(linkId, degrees, isolateType, suppressAnimation) {
  var link = this.getLinkById(linkId);
  var parentId = link.getData().getStartId();
  var childId = link.getData().getEndId();
  var parentMap = this._getNodeNetwork(parentId, degrees);
  var childMap = this._getNodeNetwork(childId, degrees);
  var nodeMap = this._mapUnion(parentMap, childMap);
  this._isolateNodeMap(nodeMap, isolateType, suppressAnimation);
};

/**
 * Isolate multiple links
 * @param {array} ids  array of link ids
 * @param {boolean} suppressAnimation whether animation should be suppressed or not
 */
dvt.AdfDiagram.prototype.isolateMultiple = function(ids, suppressAnimation) {
  var nodeMap = {};
  for (var i = 0; i < ids.length; i++) {
    var id = ids[i];
    if (id.charAt(0) === 'L') {
      var link = this.getLinkById(id);
      var parentId = link.getData().getStartId();
      var childId = link.getData().getEndId();
      var parentMap = this._getNodeNetwork(parentId, 0);
      nodeMap = this._mapUnion(nodeMap, parentMap);
      var childMap = this._getNodeNetwork(childId, 0);
      nodeMap = this._mapUnion(nodeMap, childMap);
    }
    else if (id.charAt(0) === 'N') {
      nodeMap = this._mapUnion(nodeMap, this._getNodeNetwork(id, 0));
    }
  }
  this._isolateNodeMap(nodeMap, dvt.AdfDiagram.ISOLATE_TYPE_INTERSECT, suppressAnimation);
};

dvt.AdfDiagram.prototype.isolateStack = function(nodeId, degrees, isolateType, suppressAnimation) {
  var stackName = this._nodeStackMap[nodeId];
  var stackNodes = null;
  var attributeGroups = this.getAttributeGroups();
  if (stackName) {
    stackNodes = this._stackNodeMap[stackName];
  }
  else {
    // Stack of 1
    stackName = this.CreateStackName(nodeId, attributeGroups);
    stackNodes = [nodeId];
  }
  if (stackName) {
    var nodeMap = {};
    for (var i = 0; i < stackNodes.length; i++) {
      var network = this._getNodeNetwork(stackNodes[i], degrees);
      // Check to see if any nodes in the network are new nodes belonging to this stack
      // If so, add them to the end of the list to be processed
      for (var networkId in network) {
        var networkStackName = this.CreateStackName(networkId, attributeGroups);
        if (stackName === networkStackName && dvt.ArrayUtils.getIndex(stackNodes, networkId) == -1) {
          stackNodes.push(networkId);
        }
      }
      nodeMap = this._mapUnion(nodeMap, network);
    }
    this._isolateNodeMap(nodeMap, isolateType, suppressAnimation);
  }
};

dvt.AdfDiagram.prototype._concatMap = function(map) {
  // Convert the isolate state to a string and store on the peer
  var str = '';
  var first = true;
  for (var id in map) {
    if (!first) {
      str += dvt.AdfDiagram.ROWKEY_DELIMITER;
    }
    str += id;
    str += dvt.AdfDiagram.ROWKEY_DELIMITER;
    str += map[id];
    first = false;
  }
  return str;
};

dvt.AdfDiagram.prototype.restore = function(suppressAnimation) {
  //don't need to restore drill, because you can do that through breadcrumbs
  //var oldDrilledNodePath = this._drilledNodePath;
  var oldIsolatedNodes = this._isolatedNodes;
  //this._drilledNodePath = null;
  this._isolatedNodes = null;
  this._isolatedNodeMap = null;
  //if (oldDrilledNodePath) {
  //  dvt.AdfDiagramTransitionUtils.processDrill(this);
  //}
  //else {
  if (oldIsolatedNodes) {
    if (!suppressAnimation) {
      dvt.AdfDiagramTransitionUtils.processIsolate(this);
    }
  }
  //}
  var event = dvt.EventFactory.newAdfPropertyChangeEvent(dvt.AdfDiagram.ISOLATED_NODES_KEY, this._concatMap(this._isolatedNodeMap));
  this.dispatchEvent(event);
};

/**
 * @private
 * Isolate nodes in the map
 * @param {object} nodeMap  map of nodes
 * @param {string} isolateType isolate action type
 * @param {boolean} suppressAnimation whether animation should be suppressed or not
 */
dvt.AdfDiagram.prototype._isolateNodeMap = function(nodeMap, isolateType, suppressAnimation) {
  if (isolateType === dvt.AdfDiagram.ISOLATE_TYPE_UNION) {
    this._isolatedNodeMap = this._isolatedNodeMap ? this._isolatedNodeMap : this._getAllNodeMap();
    this._isolatedNodeMap = this._mapUnion(this._isolatedNodeMap, nodeMap);
  }
  else if (isolateType === dvt.AdfDiagram.ISOLATE_TYPE_REPLACE) {
    this._isolatedNodeMap = nodeMap;
  }
  else { // dvt.AdfDiagram.ISOLATE_TYPE_INTERSECT
    if (nodeMap) {
      if (!this._isolatedNodeMap) {
        this._isolatedNodeMap = this._mapUnion(null, nodeMap);  // Intersection with universe just returns (a copy of) the original set
      }
      else {
        this._isolatedNodeMap = {};
        var ancestors = {};
        for (var key in nodeMap) {
          var curId = key;
          while (curId) {
            var curNode = this.getNodeById(curId);
            curId = curNode.getData().getGroupId();
            if (curId) {
              ancestors[curId] = curId;
            }
          }
        }
        for (var key in nodeMap) {
          if (dvt.ArrayUtils.getIndex(this._isolatedNodes, key) != -1) {
            this._isolatedNodeMap[key] = nodeMap[key];
          }
          else if (ancestors[key]) {
            this._isolatedNodeMap[key] = false;  // ancestors never need fetching
          }
        }
      }
    }
  }
  this._isolatedNodes = this._keyList(this._isolatedNodeMap);
  if (!suppressAnimation) {
    dvt.AdfDiagramTransitionUtils.processIsolate(this);
  }
  var event = dvt.EventFactory.newAdfPropertyChangeEvent(dvt.AdfDiagram.ISOLATED_NODES_KEY, this._concatMap(this._isolatedNodeMap));
  this.dispatchEvent(event);
};

dvt.AdfDiagram.prototype._keyList = function(map) {
  var keys = null;
  if (map) {
    keys = [];
    for (var key in map) {
      keys.push(key);
    }
  }
  return keys;
};

dvt.AdfDiagram.prototype._mapUnion = function(map1, map2) {
  var unionMap = {};
  if (map1) {
    for (var obj1 in map1) {
      unionMap[obj1] = map1[obj1];
    }
  }
  // Order matters, values in map2 "win"
  if (map2) {
    for (var obj2 in map2) {
      unionMap[obj2] = map2[obj2];
    }
  }
  return unionMap;
};

dvt.AdfDiagram.prototype.GetStackNodeMap = function() {
  return this._stackNodeMap;
};

dvt.AdfDiagram.prototype.GetNodeStackMap = function() {
  return this._nodeStackMap;
};

/**
 * Create stack name for the given node
 * @param {string} nodeId  diagram node id
 * @param {array} attrGroups  array of attribute group ids
 * @return {string} stack name
 * @protected
 */
dvt.AdfDiagram.prototype.CreateStackName = function(nodeId, attrGroups) {
  var stackName = '';
  var node = this.getNodeById(nodeId);
  var nodeAttrGroups = node.getData().getAttributeGroups();

  for (var i = 0; i < attrGroups.length; i++) {
    var partialStackName = null;
    if (nodeAttrGroups) {
      for (var j = 0; j < nodeAttrGroups.length; j++) {
        if (nodeAttrGroups[j].indexOf(attrGroups[i]) === 0) {
          partialStackName = nodeAttrGroups[j];
          break;
        }
      }
    }
    if (partialStackName) {
      stackName += partialStackName;
      stackName += ';';
    }
  }
  if (stackName.length == 0) {
    // Didn't match any attributeGroups so this node shouldn't be stacked
    return null;
  }
  var groupId = node.getData().getGroupId();
  if (groupId) {
    stackName = groupId + ':' + stackName;
  }

  return stackName;
};


/**
 * Helper method to create a dvt.Button
 * @param {String} iconEna The uri for the enabled button image
 * @param {String} iconOvr The uri for the hover button image
 * @param {String} iconDwn The uri for the active button image
 * @param {Number} ww The button width
 * @param {Number} hh The button height
 * @param {Number} iconOffsetY The vertical buttom image offset
 * @param {String} id The button id
 * @protected
 */
dvt.AdfDiagram.prototype.CreateButton = function(iconEna, iconOvr, iconDwn, ww, hh, iconOffsetY, id) {

  var buttonStyles = this.getDiagramButtonStyles();

  var IMAGE_SIZE = dvt.PanelDrawer.IMAGE_SIZE;
  if (!ww) {
    ww = dvt.AdfDiagram._BUTTON_SIZE;
  }
  if (!hh) {
    hh = dvt.AdfDiagram._BUTTON_SIZE;
  }

  var borderRadius = this._buttonBorderRadius;

  var ena = new dvt.Rect(this.getCtx(), 0, 0, ww, hh);
  ena.setRx(borderRadius);
  ena.setRy(borderRadius);
  var fill = dvt.AdfDiagram.getButtonFill(buttonStyles, dvt.AdfDiagram.BUTTON_STATE_ENA);
  var stroke = dvt.AdfDiagram.getButtonStroke(buttonStyles, dvt.AdfDiagram.BUTTON_STATE_ENA);

  ena.setFill(fill);
  ena.setStroke(stroke);
  if (iconEna) {
    var icon = new dvt.Image(this.getCtx(), iconEna, 0, 0, IMAGE_SIZE, IMAGE_SIZE);
    ena.addChild(icon);
    icon.setTranslate(.5 * (ww - icon.getWidth()), iconOffsetY + .5 * (hh - iconOffsetY - icon.getHeight()));
  }

  var ovr = new dvt.Rect(this.getCtx(), 0, 0, ww, hh);
  ovr.setRx(borderRadius);
  ovr.setRy(borderRadius);
  fill = dvt.AdfDiagram.getButtonFill(buttonStyles, dvt.AdfDiagram.BUTTON_STATE_OVR);
  stroke = dvt.AdfDiagram.getButtonStroke(buttonStyles, dvt.AdfDiagram.BUTTON_STATE_OVR);
  ovr.setFill(fill);
  ovr.setStroke(stroke);
  if (iconOvr) {
    var icon = new dvt.Image(this.getCtx(), iconOvr, 0, 0, IMAGE_SIZE, IMAGE_SIZE);
    ovr.addChild(icon);
    icon.setTranslate(.5 * (ww - icon.getWidth()), iconOffsetY + .5 * (hh - iconOffsetY - icon.getHeight()));
  }

  var dwn = new dvt.Rect(this.getCtx(), 0, 0, ww, hh);
  dwn.setRx(borderRadius);
  dwn.setRy(borderRadius);

  fill = dvt.AdfDiagram.getButtonFill(buttonStyles, dvt.AdfDiagram.BUTTON_STATE_DWN);
  stroke = dvt.AdfDiagram.getButtonStroke(buttonStyles, dvt.AdfDiagram.BUTTON_STATE_DWN);
  dwn.setFill(fill);
  dwn.setStroke(stroke);
  if (iconDwn) {
    var icon = new dvt.Image(this.getCtx(), iconDwn, 0, 0, IMAGE_SIZE, IMAGE_SIZE);
    dwn.addChild(icon);
    icon.setTranslate(.5 * (ww - icon.getWidth()), iconOffsetY + .5 * (hh - iconOffsetY - icon.getHeight()));
  }

  var button = new dvt.Button(this.getCtx(), ena, ovr, dwn, null, id);
  button.setPixelHinting(true);
  return button;
};


/**
 * Creates the restore button which is shown after isolating on a node
 * @protected
 * @return {dvt.Button} The diagram restore button
 */
dvt.AdfDiagram.prototype.CreateRestoreButton = function() {
  var restoreEna = this.getResourcesMap()['restoreEna'];
  var restoreOvr = this.getResourcesMap()['restoreOvr'];
  var restoreDwn = this.getResourcesMap()['restoreDwn'];
  var restoreTip = this.getResourcesMap()['restoreTip'];
  var button = this.CreateButton(restoreEna, restoreOvr, restoreDwn,
                                 dvt.AdfDiagram._WIDE_BUTTON_SIZE, dvt.AdfDiagram._BUTTON_SIZE + this._buttonBorderRadius,
                                 this._buttonBorderRadius, 'restore');

  var buttonHandler = {};
  buttonHandler.getTooltip = function() {
    return restoreTip;
  };
  var thisRef = this;
  buttonHandler.HandleClick = function(evt) {
    //Bug 13381888: consume the event
    dvt.EventManager.consumeEvent(evt);
    thisRef.HandleRestore();
  };
  //Bug 13381888: clicking on button shouldn't affect selection
  buttonHandler.isUnrelatedToSelection = function() {
    return true;
  };
  this.getEventManager().associate(button, buttonHandler);

  return button;
};

/**
 * Handles a restore event
 *
 * @param {dvt.BaseEvent} evt
 * @param {dvt.Button} source
 * @param {boolean} suppressAnimation whether or not animation should be suppressed
 */
dvt.AdfDiagram.prototype.HandleRestore = function(evt, source, suppressAnimation) {
  dvt.EventManager.consumeEvent(evt);
  this.restore(suppressAnimation);
  this.HideRestoreButton();
};

dvt.AdfDiagram.prototype.ShowRestoreButton = function() {
  if (this._restoreButton || !dvt.Agent.isEnvironmentBrowser() || this._showRestore == 'off') { //Don't render the button for non browser enviornment because it is meant for printing.
    return;
  }

  this._restoreButton = new dvt.Container(this.getCtx());
  var button = this.CreateRestoreButton();
  this._restoreButton.addChild(button);
  this.addChild(this._restoreButton);
  var dims = this._restoreButton.getDimensions();
  //translate the button inside its container so that the container doesn't
  //need to know about this translation
  button.setTranslateY(-this._buttonBorderRadius);
  //clip the button in the coordinate space of its container
  var pad = 5; //allow for border around button
  button.setClipPath(this.CreateClipRect(-pad, 0, dims.w + (2 * pad), dims.h + pad, 'restoreButtonClip'));

  //adjust the button when breadcrumbs are shown
  var topMargin = this.getPanZoomCanvas().getTranslateY();
  this._restoreButton.setTranslate(.5 * (this.getWidth() - (dims.x + dims.w)), topMargin);

  this._restoreButton.setAlpha(0);
  var anim = new dvt.Animator(this.getCtx(), .25);
  anim.addProp(dvt.Animator.TYPE_NUMBER, this._restoreButton, this._restoreButton.getAlpha, this._restoreButton.setAlpha, 1);
  anim.play();
};

dvt.AdfDiagram.prototype.HideRestoreButton = function() {
  if (this._restoreButton)
  {
    var anim = new dvt.Animator(this.getCtx(), .25);
    anim.addProp(dvt.Animator.TYPE_NUMBER, this._restoreButton, this._restoreButton.getAlpha, this._restoreButton.setAlpha, 0);

    anim.setOnEnd(this.RemoveRestoreButton, this);

    anim.play();
  }
};

dvt.AdfDiagram.prototype.RemoveRestoreButton = function() {
  if (this._restoreButton)
  {
    this._restoreButton.destroy();
    this._restoreButton.getParent().removeChild(this._restoreButton);
    this._restoreButton = null;
  }
  // Bug 14813675 - tooltips get stuck for isolate button and other buttons
  var tooltipManager = this.getCtx().getTooltipManager();
  if (tooltipManager) {
    tooltipManager.hideTooltip();
  }
};


/**
 * Used by automation APIs
 */
dvt.AdfDiagram.prototype.getRestoreButton = function() {
  return this._restoreButton;
};


/**
 * @protected
 * Calculate the offset needed to pan the given node into the viewport.
 * @param  {string}  nodeId  The id of the node to pan into view.
 * @return  {dvt.Point}  Offset needed to pan the node into view.
 */
dvt.AdfDiagram.prototype.CalcOffsetToPanNodeIntoView = function(nodeId, animator) {
  var nodeBounds = this.GetViewBounds(animator, [nodeId], []);
  var viewport = this.getPanZoomCanvas().getViewport();
  //need to scale the zoom-to-fit padding by the inverse of the zoom
  var ztfPadding = this.getPanZoomCanvas().getZoomToFitPadding() /
                   this.getPanZoomCanvas().getZoom();
  viewport.x += ztfPadding;
  viewport.y += ztfPadding;
  viewport.w -= 2 * ztfPadding;
  viewport.h -= 2 * ztfPadding;
  var diffX = 0;
  var diffY = 0;
  //check whether right side of node is getting cut off first
  if (nodeBounds.x + nodeBounds.w > viewport.x + viewport.w) {
    diffX += viewport.x + viewport.w - (nodeBounds.x + nodeBounds.w);
    viewport.x -= diffX;
  }
  //after adjusting for right side of node, make sure left side of
  //node is not cut off
  if (nodeBounds.x < viewport.x) {
    diffX += viewport.x - nodeBounds.x;
    viewport.x -= diffX;
  }
  //check whether bottom of node is getting cut off first
  if (nodeBounds.y + nodeBounds.h > viewport.y + viewport.h) {
    diffY += viewport.y + viewport.h - (nodeBounds.y + nodeBounds.h);
    viewport.y -= diffY;
  }
  //after adjusting for bottom of node, make sure top of
  //node is not cut off
  if (nodeBounds.y < viewport.y) {
    diffY += viewport.y - nodeBounds.y;
    viewport.y -= diffY;
  }
  return new dvt.Point(diffX, diffY);
};

/**
 * @protected
 * Calculate the offset needed to keep the given node in the same place.
 * @param  {string}  nodeId  The id of the node to pan into view.
 * @return  {dvt.Point}  Offset needed to pan the node into view.
 */
/*dvt.AdfDiagram.prototype.CalcOffsetToKeepNodeInPlace = function(nodeId, animator) {
  if (!animator) {
    return null;
  }

  var node = this.getNodeById(nodeId);
  var oldTx = node.getTranslateX();
  var oldTy = node.getTranslateY();

  var newTx = animator.getDestVal(node, node.getTranslateX);
  var newTy = animator.getDestVal(node, node.getTranslateY);

  var diffX = newTx - oldTx;
  var diffY = newTy - oldTy;

  //map dims to contentPane of panZoomCanvas if node in container
  if (node.getData().getGroupId()) {
    var nodeParent = node.getParent();
    var contentPane = this.getPanZoomCanvas().getContentPane();
    var stageOld = nodeParent.localToStage(new dvt.Point(oldTx, oldTy));
    var localOld = contentPane.stageToLocal(stageOld);

    //need to manually walk container tree because positions
    //will be in animator
    var stageNew = this.LocalToStage(new dvt.Point(newTx, newTy), nodeParent, animator);
    var localNew = this.StageToLocal(stageNew, contentPane, animator);
    diffX = localNew.x - localOld.x;
    diffY = localNew.y - localOld.y;
  }

  return new dvt.Point(-diffX, -diffY);
};*/

/*
 * Begin editor additions
 */
dvt.AdfDiagram.prototype.getEditor = function() {
  return this._editor;
};

/**
 * Specifies whether this diagram is read-only
 * @param {boolean} readOnly true if diagram is readonly
 */
dvt.AdfDiagram.prototype.setReadOnly = function(readOnly)
{
  if (!readOnly) {
    this._editor = new DvtAdfDiagramEditorManager(this.getCtx(), this);
  } else {
    this._editor = null;
  }
  this._readOnly = readOnly;
};

/**
 * Checks whether this diagram is read-only
 * @return {boolean} true if diagram is readonly
 */
dvt.AdfDiagram.prototype.isReadOnly = function() {
  return this._readOnly;
};


dvt.AdfDiagram.prototype.setHasDragSource = function(hasDragSource)
{
  this._hasDragSource = hasDragSource;
};

dvt.AdfDiagram.prototype.setLinkToolEnabled = function(enabled)
{
  this._linkToolEnabled = enabled;
};

dvt.AdfDiagram.prototype.isLinkToolEnabled = function()
{
  return this._linkToolEnabled;
};

dvt.AdfDiagram.prototype.setStickyNodeEnabled = function(enabled)
{
  this._stickyNodeEnabled = enabled;
};

dvt.AdfDiagram.prototype.isStickyNodeEnabled = function()
{
  return this._stickyNodeEnabled;
};

dvt.AdfDiagram.prototype.setNodeCreateEnabled = function(enabled)
{
  this._nodeCreateEnabled = enabled;
};

dvt.AdfDiagram.prototype.isNodeCreateEnabled = function()
{
  return this._nodeCreateEnabled;
};

dvt.AdfDiagram.prototype.setLinkCreateEnabled = function(enabled)
{
  this._linkCreateEnabled = enabled;
};

dvt.AdfDiagram.prototype.isLinkCreateEnabled = function()
{
  return this._linkCreateEnabled;
};

dvt.AdfDiagram.prototype.setCreateLinkDef = function(paletteId, linkDef) {
  this._createLinkDef = linkDef;
  this._createLinkPaletteId = paletteId;
  this.addLinkDef(linkDef);
};

dvt.AdfDiagram.prototype.getCreateLinkData = function() {
  if (!this._createLinkDef) {
    var linkDef = new dvt.AdfDiagramLinkDef();
    linkDef.setId('FakeLinkDef');
    linkDef.setLinkWidth('3px');
    linkDef.setLinkColor('green');
    linkDef.setLinkStyle(dvt.AdfDiagramLinkDef.LINK_STYLE_DOT);
    this.setCreateLinkDef('fake', linkDef);
  }
  var ret = new Object();
  ret.linkDef = this._createLinkDef;
  ret.paletteId = this._createLinkPaletteId;
  return ret;
};

dvt.AdfDiagram.prototype.setCreateNodeStamp = function(paletteId, stamp) {
  var nodeDef = this.createNodeDef(stamp);
  this._createNodeDef = nodeDef;
  this._createNodePaletteId = paletteId;
};

dvt.AdfDiagram.prototype.getCreateNodeData = function() {
  var ret = new Object();
  ret.nodeDef = this._createNodeDef;
  ret.paletteId = this._createNodePaletteId;
  return ret;
};


/**
 * Handles palette selection change event
 * @param {object} event Palette selection event
 */
dvt.AdfDiagram.prototype.handlePaletteEvent = function(event) 
{
  var type = event['type'];
  if (type == 'selection') {
    var selectionArray = event['selection'];
    if (selectionArray && selectionArray.length >= 1) {
      this.setStickyNodeEnabled(false);
      this.setLinkToolEnabled(false);
      for (var i = 0; i < selectionArray.length; i++) {
        var data = selectionArray[i];
        var paletteId = data.paletteId;
        var itemId = data.id;
        var stamp = data.def;
        if (data.type == DvtPaletteLinkItem.LINK_TYPE) {
          this.setLinkToolEnabled(true);
          this.setCreateLinkDef(paletteId, stamp);

          //store the selected link on a peer
          var event = dvt.EventFactory.newAdfPropertyChangeEvent(dvt.Palette.PALETTE_ACTIVE_LINK_KEY, itemId);
          this.dispatchEvent(event);
        } else if (data.type == DvtPaletteLinkItem.NODE_TYPE) {
          this.setCreateNodeStamp(paletteId, stamp);
          this.setStickyNodeEnabled(true);
        }
      }
    } else {
      this.setStickyNodeEnabled(false);
      this.setLinkToolEnabled(false);
    }
  }
};


dvt.AdfDiagram.prototype.createNodeDef = function(stamp) {
  var nodeDef = new dvt.AdfDiagramNodeDef();
  nodeDef.setId('FakeNodeDef' + this._fakeNodeDefCount);
  this._fakeNodeDefCount++;
  nodeDef.setNodeTemplate(stamp, 'zoom100');
  nodeDef.setNodeTemplate(stamp, 'zoom75');
  nodeDef.setNodeTemplate(stamp, 'zoom50');
  nodeDef.setNodeTemplate(stamp, 'zoom25');
  this.addNodeDef(nodeDef);
  return nodeDef;
};

/**
 * Sends an event for creating a new diagram node out of dropped palette object.
 * The node is created during drag and drop operation from internal palette to the diagram.
 * The method is called by AdfDhtmlDiagramPeer on "dropPerformed" from internal palette.
 *
 * @param {number} x x-coordinate for the drop
 * @param {number} y y-coordinate for the drop
 * @param {string} paletteId palette object id
 * @param {object} stamp template for the palette object
 */
dvt.AdfDiagram.prototype.createDropNode = function(x, y, paletteId, stamp) 
{
  if (this._editor) {

    // Create and fire the event
    var pos = new dvt.Point(x, y);
    var relPos = this.getCtx().pageToStageCoords(pos.x, pos.y);

    var parentId = null;
    var dropSite = this.getDropSite(relPos.x, relPos.y);
    if (dropSite) {
      parentId = dropSite.clientRowKey ? 'N' + dropSite.clientRowKey : null;
    }

    var evt = dvt.AdfDiagramEventManager.GetCreateNodeEvent(parentId, relPos, paletteId);
    this.dispatchEvent(evt);
  }
};


/**
 * Returns the node under the specified coordinates.
 * @param {number} x
 * @param {number} y
 * @return {dvt.AdfDiagramNode}
 */
dvt.AdfDiagram.prototype.__getNodeUnderPoint = function(x, y) {
  var nodes = this._getAllNodeObjects();
  var mousePos = new dvt.Point(x, y);
  var dropTarget = null;
  for (var i = 0; i < nodes.length; i++) {
    var node = nodes[i];
    if (!node.getVisible())
      continue;
    var nodeBounds = node.getLayoutBounds();
    var relMousePos = node.stageToLocal(mousePos);
    if (nodeBounds.containsPoint(relMousePos.x, relMousePos.y)) {
      dropTarget = node;
      //break;
    }
  }

  return dropTarget;
};


/**
 * Returns the clientId of the drag source owner if dragging is supported.
 * @param {array} clientIds
 * @return {string}
 */
dvt.AdfDiagram.prototype.__isDragAvailable = function(clientIds) {
  if (!this._hasDragSource)
    return null;
  // Drag and drop supported when selection is enabled, only 1 drag source
  if (this._selectionHandler)
    return clientIds[0];
  else
    return null;
};


/**
 * Returns the row keys for the current drag.
 * @param {DvtBaseTreeNode} node The node where the drag was initiated.
 * @return {array} The row keys for the current drag.
 */
dvt.AdfDiagram.prototype.__getDragTransferable = function(node) {
  // Select the node if not already selected
  if (!node.isSelected()) {
    this._selectionHandler.processClick(node, false);
    this.EventManager.fireSelectionEvent();
  }

  // Gather the rowKeys for the selected objects
  var rowKeys = [];
  var selection = this._selectionHandler.getSelection();
  for (var i = 0; i < selection.length; i++) {
    var id = selection[i].getId();
    this.__addRowKeyToDragTransferable(id, rowKeys);
  }

  return rowKeys;
};

dvt.AdfDiagram.prototype.__addRowKeyToDragTransferable = function(rowKey, arRowKeys) {
  if (rowKey.substring(0, 1) == 'N') {
    arRowKeys.push(rowKey.substring(1));
  }
};


/**
 * Returns the displayables to use for drag feedback for the current drag.
 * @param {DvtBaseTreeNode} node The node where the drag was initiated.
 * @return {array} The displayables for the current drag.
 */
dvt.AdfDiagram.prototype.__getDragFeedback = function() {
  // This is called after __getDragTransferable, so the selection has been updated already.
  // Gather the displayables for the selected objects
  var displayables = [];
  var selection = this._selectionHandler.getSelection();
  for (var i = 0; i < selection.length; i++) {
    this.__addDisplayableToDragFeedback(selection[i].__getDisplayable(), displayables);
  }

  return displayables;
};

dvt.AdfDiagram.prototype.__addDisplayableToDragFeedback = function(displayable, arDisplayables) {
  arDisplayables.push(displayable);
};

dvt.AdfDiagram.prototype.__removeDropSiteFeedback = function() {
  // Remove any existing drop site feedback
  if (this._dropSiteFeedback) {
    this._dropSiteFeedback.getParent().removeChild(this._dropSiteFeedback);
    this._dropSiteFeedback = null;
  }
};


/**
 * Displays drop site feedback for the specified node.
 * @param {DvtBaseTreeNode} node The node for which to show drop feedback, or null to remove drop feedback.
 * @return {dvt.Displayable} The drop site feedback, if any.
 */
dvt.AdfDiagram.prototype.__showDropSiteFeedback = function(node) {
  // Remove any existing drop site feedback
  this.__removeDropSiteFeedback();

  // Create feedback for the node
  if (node) {
    this._dropSiteFeedback = node.getDropSiteFeedback();
  } else {
    this._dropSiteFeedback = new dvt.Rect(this.getCtx(), 0, 0, this.Width, this.Height);
  }

  if (this._dropSiteFeedback) {
    var fillColor = this.getDropFeedbackFillColor();
    var fillAlpha = this.getDropFeedbackFillAlpha();
    var borderColor = this.getDropFeedbackBorderColor();
    this._dropSiteFill = new dvt.SolidFill(fillColor, fillAlpha);
    this._dropSiteStroke = new dvt.SolidStroke(borderColor);
    this._dropSiteFeedback.setFill(this._dropSiteFill);
    this._dropSiteFeedback.setStroke(this._dropSiteStroke);

    if (node)
      this.addChild(this._dropSiteFeedback);
    else
      this.addChildAt(this._dropSiteFeedback, 1);

  }

  return this._dropSiteFeedback;
};

dvt.AdfDiagram.prototype.setDropFeedbackFillColor = function(color) {
  this._dropFeedbackFillColor = color;
};

dvt.AdfDiagram.prototype.getDropFeedbackFillColor = function() {
  return this._dropFeedbackFillColor;
};

dvt.AdfDiagram.prototype.setDropFeedbackBorderColor = function(color) {
  this._dropFeedbackBorderColor = color;
};

dvt.AdfDiagram.prototype.getDropFeedbackBorderColor = function() {
  return this._dropFeedbackBorderColor;
};

dvt.AdfDiagram.prototype.setDropFeedbackFillAlpha = function(alpha) {
  this._dropFeedbackFillAlpha = alpha;
};

dvt.AdfDiagram.prototype.getDropFeedbackFillAlpha = function() {
  return this._dropFeedbackFillAlpha;
};

dvt.AdfDiagram.prototype.Log = function(msg, level) {
  this.dispatchEvent(dvt.EventFactory.newAdfLogEvent(level, msg));
};


/**
 * Sets diagram resources - tooltips and images
 * @param {dvt.XmlNode} uris XML node that contains component resources
 */
dvt.AdfDiagram.prototype.setResources = function(uris) {
  this._addToResourcesMap(uris);
  var childNodes = uris.getChildNodes();
  for (var i = 0; i < childNodes.length; i++) {
    var node = childNodes[i];
    var nodeName = node.getName();
    if (nodeName == dvt.AdfDiagram._ELEM_RESOURCES_LEGEND)
      this._addToResourcesMap(node);
    else if (nodeName == dvt.AdfDiagram._ELEM_RESOURCES_PALETTE)
      this._addToResourcesMap(node);
    else if (nodeName == dvt.AdfDiagram._ELEM_RESOURCES_SEARCHPANEL)
      this._addToResourcesMap(node);
    else if (nodeName == dvt.AdfDiagram._ELEM_RESOURCES_PANELDRAWER) {
      this._addToResourcesMap(node);
    }
    else if (nodeName == dvt.AdfDiagram._ELEM_RESOURCES_CONTROLPANEL) {
      this._addToResourcesMap(node);
    }
    else if (nodeName == dvt.AdfDiagram._ELEM_RESOURCES_TRANSIENT_TOOLBAR) {
      this._addToResourcesMap(node);
    }
    else if (nodeName == dvt.AdfDiagram._ELEM_RESOURCES_NODE) {
      this._addToResourcesMap(node);
    }
  }
};

dvt.AdfDiagram.prototype._addToResourcesMap = function(xmlNode) {
  var arAttrs = xmlNode.getAttributes();
  for (var j = 0; j < arAttrs.length; j++) {
    var pair = arAttrs[j];
    this.getResourcesMap()[pair.name] = pair.value;
  }
};

dvt.AdfDiagram.prototype.AdjustViewSpace = function(topMargin, animator) {
  //adjust the view space when breadcrumbs are shown
  this.getPanZoomCanvas().setTranslateY(topMargin);
  this.getPanZoomCanvas().setSize(this.getWidth(), this.getHeight() - topMargin);
  if (this._panelDrawer) {
    this._panelDrawer.setTranslateY(topMargin);
  }
  if (this._restoreButton) {
    this._restoreButton.setTranslateY(topMargin);
  }
};

dvt.AdfDiagram.prototype.CreateClipRect = function(x, y, ww, hh, id) {
  var clipPath = new dvt.ClipPath(id);
  clipPath.addRect(x, y, ww, hh);
  return clipPath;
};

/**
 * @protected
 * Update bread crumbs upon drill
 * @param {dvt.Animator} animator Optional animator for the component that is used to animate transition
 */
dvt.AdfDiagram.prototype.UpdateDrillBreadcrumbs = function(animator) {
  //remove breadcrumbs
  if (!this._drilledNodePath) {
    if (this._drillBreadcrumbs) {
      var thisRef = this;
      //adjust the view space after removing breadcrumbs
      var pzcRestoreFunc = function() {
        thisRef.AdjustViewSpace(0);
      };
      if (animator) {
        //fade out and remove at end
        animator.addProp(dvt.Animator.TYPE_NUMBER, this._drillBreadcrumbs,
                         this._drillBreadcrumbs.getAlpha,
                         this._drillBreadcrumbs.setAlpha, 0);
        dvt.Playable.appendOnEnd(animator, this.RemoveDrillBreadcrumbs, this);
        dvt.Playable.appendOnEnd(animator, pzcRestoreFunc);
      }
      else {
        //remove immediately
        this.RemoveDrillBreadcrumbs();
        pzcRestoreFunc();
      }
    }
  }
  //show breadcrumbs
  else {
    var bAnimatePosition = true;
    //save old breadcrumbs for animating transition
    this._oldDrillBreadcrumbs = this._drillBreadcrumbs;

    //create new breadcrumbs
    var options = this.getBreadcrumbsOptions();
    this._drillBreadcrumbs = new dvt.Breadcrumbs(this.getCtx(), this._processDrillBreadcrumbsEvent, this, options);

    var breadcrumbsX = 5;
    //set breadcrumbs Id based on component id
    this._drillBreadcrumbs.setId(this.getId() + '_bc');
    this.addChild(this._drillBreadcrumbs);
    this._drillBreadcrumbs.setTranslateX(breadcrumbsX);

    if (this._oldDrillBreadcrumbs)
      this.EventManager.removeComponentKeyboardHandler(this._oldDrillBreadcrumbs.getEventManager());

    if (this._drillBreadcrumbs)
      this.EventManager.addComponentKeyboardHandlerAt(this._drillBreadcrumbs.getEventManager(), 0);

    if (animator) {
      //fade in new breadcrumbs
      this._drillBreadcrumbs.setAlpha(0);
      animator.addProp(dvt.Animator.TYPE_NUMBER, this._drillBreadcrumbs,
                       this._drillBreadcrumbs.getAlpha,
                       this._drillBreadcrumbs.setAlpha, 1);
    }

    if (!this._oldDrillBreadcrumbs) {
      //if breadcrumbs are appearing for the first time, don't need
      //to animate translation
      bAnimatePosition = false;
    }
    else {
      //if breadcrumbs are changing, put the new breadcrumbs at the
      //same position as the old ones
      this._drillBreadcrumbs.setTranslateY(this._oldDrillBreadcrumbs.getTranslateY());
      if (animator) {
        //fade out the old breadcrumbs
        animator.addProp(dvt.Animator.TYPE_NUMBER, this._oldDrillBreadcrumbs,
                         this._oldDrillBreadcrumbs.getAlpha,
                         this._oldDrillBreadcrumbs.setAlpha, 0);
      }
    }

    var data = {};
    data.items = [];
    //add an initial breadcrumb for showing the whole diagram
    var dataItem = {};
    dataItem['id'] = null;
    dataItem['label'] = this.getResourcesMap()['drillRoot'];
    data.items.push(dataItem);
    for (var i = 0; i < this._drilledNodePath.length; i++) {
      dataItem = {};
      var nodeId = this._drilledNodePath[i];
      dataItem.id = nodeId;
      var node = this.getNodeById(nodeId);
      //for breadcrumb label, try to use:
      //1) label attribute
      //2) shortDesc
      //3) substitute a default placeholder
      var label = node.getLabel();
      if (!label) {
        label = node.getShortDesc();
      }
      if (!label) {
        label = '...';
      }
      dataItem['label'] = label;
      data.items.push(dataItem);
    }
    //TO DO: take overview window into account for available width
    this._drillBreadcrumbs.render(data, this.Width - 2 * breadcrumbsX);
    var dims = this._drillBreadcrumbs.getDimensions();
    var breadcrumbsY = 5;
    var breadcrumbsMarginBottom = 10;
    var newY = breadcrumbsY;
    //shift the rest of the view down when breadcrumbs are shown
    this.AdjustViewSpace(newY + dims.y + dims.h + breadcrumbsMarginBottom, animator);
    if (animator && bAnimatePosition) {
      //animate the translation to the new position
      animator.addProp(dvt.Animator.TYPE_NUMBER, this._drillBreadcrumbs,
                       this._drillBreadcrumbs.getTranslateY,
                       this._drillBreadcrumbs.setTranslateY, newY);
      if (this._oldDrillBreadcrumbs) {
        //animate the old breadcrumbs with the new, so that it looks
        //like it's the same breadcrumbs moving to a new place as they fade
        animator.addProp(dvt.Animator.TYPE_NUMBER, this._oldDrillBreadcrumbs,
                         this._oldDrillBreadcrumbs.getTranslateY,
                         this._oldDrillBreadcrumbs.setTranslateY, newY);
        //remove the old breadcrumbs after the animation
        dvt.Playable.appendOnEnd(animator, this.RemoveOldDrillBreadcrumbs, this);
      }
    }
    else {
      //move the breadcrumbs to the correct location
      this._drillBreadcrumbs.setTranslateY(newY);
      if (this._oldDrillBreadcrumbs) {
        //remove the old breadcrumbs
        this.RemoveOldDrillBreadcrumbs();
      }
    }
  }
};


/**
 * Remove drill breadcrumbs
 * @protected
 */
dvt.AdfDiagram.prototype.RemoveDrillBreadcrumbs = function() {
  this.EventManager.removeComponentKeyboardHandler(this._drillBreadcrumbs.getEventManager());
  this._drillBreadcrumbs.getParent().removeChild(this._drillBreadcrumbs);
  this._drillBreadcrumbs = null;
};

dvt.AdfDiagram.prototype.RemoveOldDrillBreadcrumbs = function() {
  this._oldDrillBreadcrumbs.getParent().removeChild(this._oldDrillBreadcrumbs);
  this._oldDrillBreadcrumbs = null;
};

dvt.AdfDiagram.prototype._processDrillBreadcrumbsEvent = function(event) {
  if (event instanceof dvt.BreadcrumbsDrillEvent)
    this.drillNode(event.getId());
};


/**
 * Used by automation APIs
 */
dvt.AdfDiagram.prototype.getBreadcrumbs = function() {
  return this._drillBreadcrumbs;
};

/**
 * @override
 * Load JSON object
 * @param {object} jsonObj  Diagram JSON object
 * @param {String} eventType  Event type of the response
 * @param {object} paramKeys  Param keys
 * @param {object} paramValues  Param values
 */
dvt.AdfDiagram.prototype.loadJson = function(jsonObj, eventType, paramKeys, paramValues) {
  if (jsonObj == null) {
    // Don't parse null/empty xml strings coming from resize notify
    return;
  }
  dvt.AdfDiagram.superclass.loadJson.call(this, jsonObj, eventType, paramKeys, paramValues);
};

dvt.AdfDiagram.prototype.ShowNodeTransientToolbar = function(node, event) {
  this.HideNodeTransientToolbar();
  if (node.IsTransientToolbarOnClick()) {
    var positionHolder = event;
    // event might be a mouse event or a touch event
    if (event instanceof dvt.ComponentTouchEvent) {
      positionHolder = event.touch;
    }
    var stagePos = this.getCtx().pageToStageCoords(positionHolder.pageX, positionHolder.pageY);
    node.ShowTransientToolbar(stagePos);
  }
};

/**
 * @protected
 * Hide transient toolbar
 * @param {boolean} bImmediate  true to hide the toolbar immediately
 */
dvt.AdfDiagram.prototype.HideNodeTransientToolbar = function(bImmediate) {
  var currId = this.GetTransientToolbarDataId();
  if (currId) {
    var currNode = this.getNodeById(currId);
    if (currNode) {
      if (currNode.IsTransientToolbarOnClick()) {
        currNode.HideTransientToolbar(bImmediate);
      }
    }
  }
};

dvt.AdfDiagram.prototype.HideNodeFlyOuts = function(bImmediate) {
  this.HideNodeTransientToolbar(bImmediate);
};

dvt.AdfDiagram.prototype.SetTransientToolbarDataId = function(dataId) {
  this._transientToolbarDataId = dataId;
};

dvt.AdfDiagram.prototype.GetTransientToolbarDataId = function() {
  return this._transientToolbarDataId;
};

dvt.AdfDiagram.prototype.SetShowRestore = function(showRestore) {
  this._showRestore = showRestore;
};

dvt.AdfDiagram.prototype.GetShowRestore = function() {
  return this._showRestore;
};

dvt.AdfDiagram.prototype.setCenterNode = function(centerNode) {
  this._centerNode = centerNode;
};

dvt.AdfDiagram.prototype.getCenterNode = function() {
  return this._centerNode;
};


//Bug 16717528 - diagram nodes overlapped if images are not loaded
dvt.AdfDiagram.prototype.addToAfImageNotReadyList = function(node, url) {
  if (! this._imageNotReadyList) {
    this._imageNotReadyList = [];
  }

  this._imageNotReadyList.push({'url': url,
    'nodeId': node.getData().getId(),
    'zoom': node.getZoom()});
};


/**
 * Bug 13536792 - images without explicit size fail to render
 * This method is called when an af image is loaded from the server
 * @param {object}  Image object
 */
dvt.AdfDiagram.prototype.onAfImageLoaded = function(image) {

  //check to see if we need to re render zoom 100
  if (this._imageNotReadyList && this._imageNotReadyList.length > 0) {

    if (! this._renderZoom100Nodes)
      this._renderZoom100Nodes = {};

    for (var i = this._imageNotReadyList.length - 1; i >= 0; i--) {
      if (this._imageNotReadyList[i].url == image.url) {
        if (this._imageNotReadyList[i].zoom == 1) {

          var nodeId = this._imageNotReadyList[i].nodeId;
          this._renderZoom100Nodes[nodeId] = this.getNodeById(nodeId);
        }

        //remove node from the wait list
        this._imageNotReadyList.splice(i, 1);
      }
    }
    //Enable Animation timer only on browser environment
    if (dvt.Agent.isEnvironmentBrowser()) {
      if (! this._imageLoadTimer || ! this._imageLoadTimer.isRunning()) {
        this.setInteractionEnabled(false);
      }
      this.StopImageLoadTimer();
      this.StartImageLoadTimer();
    } else {
      this.RenderComponentInternal();
    }
  }
};

dvt.AdfDiagram.prototype.StopImageLoadTimer = function()
{
  if (this._imageLoadTimer)
  {
    this._imageLoadTimer.stop();
    this._imageLoadTimer = null;
  }
};

/**
 * Starts the image load timer for images without explicit size.
 * @protected
 */
dvt.AdfDiagram.prototype.StartImageLoadTimer = function()
{
  //TODO: restart timer here, when the timer goes off, re-render the diagram
  this._imageLoadTimer = new dvt.Timer(this.getCtx(), 1000, this.HandleImageLoadTimer, this, 1);
  this._imageLoadTimer.start();
};

/**
 * A handler for the image load timer - rerenders diagram when all images are loaded
 * @protected
 */
dvt.AdfDiagram.prototype.HandleImageLoadTimer = function()
{
  this.StopImageLoadTimer();
  this._bAfImageLoaded = true; //set false after RenderComponentInternal

  //if reRenderTimer is on, don't re-render diagram immediate
  if (! this.RerenderTimer || ! this.RerenderTimer.isRunning()) {

    var animator = new dvt.Animator(this.getCtx(), this.getAnimationDuration());
    var thisRef = this;
    dvt.Playable.appendOnEnd(animator, function() {thisRef.setInteractionEnabled(true)});

    //re-render diagram after images loaded
    this._clearPanningConstraints();
    this._syncOverviewAndPanZoom = true;
    this.RenderComponentInternal(animator);
    this._syncOverviewAndPanZoom = false;
    animator.play();
  }
  else {
    this.setInteractionEnabled(true);
  }
};

dvt.AdfDiagram.prototype.startParsingData = function() {
  if (this._bRendered) {
    this._newNodes = [];
  }
};


/**
 * Stop parsing data
 * @public
 */
dvt.AdfDiagram.prototype.stopParsingData = function() {
  if (this._bRendered) {
    if (this._newNodes.length > 0) {
      var afContext = this.createAfContext();
      for (var i = 0; i < this._newNodes.length; i++) {
        this._newNodes[i].SetAlpha(0); // Assume there's an animation coming that will fade the node in
        this._newNodes[i].setForceZoom100Facet(true);
        this._newNodes[i].createNodeContents(afContext);
      }

      for (var i = 0; i < this._newNodes.length; i++) {
        this._newNodes[i].renderNode(afContext);
      }
      for (var i = 0; i < this._newNodes.length; i++) {
        this._newNodes[i].setForceZoom100Facet(false);
        if (this._eventType == 'dvtDiagramFetchEvent') {
          this._newNodes[i].setVisible(false); // Nodes will be made visible by the disclosure transition
        }
      }
    }
  }
  this._newNodes = null;
  if (this._drillRoot) {
    var drillNode = this.getNodeById(this._drillRoot);
    var allowDrill = drillNode && drillNode.isContainer() && drillNode.getData().isChildrenFetched();
    if (allowDrill) {
      this._drilledNodePath = this._createDrillPath(this._drillRoot);
    }
    this._drillRoot = null;
  }
};


//Bug 13350809:
/**
 * Set whether the hover window should be prevented.
 *
 * @param {boolean} bPrevent true to prevent the hover window, false to allow it
 */
dvt.AdfDiagram.prototype.setPreventNodeDetailWindow = function(bPrevent) {
  this._bPreventNodeDetailWindow = bPrevent;
  //TO DO: uncomment and implement this
  /*if (bPrevent) {
    var node = this.getNodeWithHoverWindow();
    if (node) {
      node.getRootNode().stopHoverWindow(false);
    }
    node = getNodeTriggeringHoverWindow();
    if (node) {
      node.getRootNode().stopHoverWindow(false);
    }
  }*/
};


/**
 * Determine whether the hover window should be prevented.
 *
 * @return {boolean} true to prevent the hover window, false to allow it
 */
dvt.AdfDiagram.prototype.isPreventNodeDetailWindow = function() {
  return this._bPreventNodeDetailWindow;
};

/**
 * Get the search panel component
 * @param {boolean} bCreate true to create search panel if search panel don't exist
 * @return {dvt.SearchPanel} search panel component
 */
dvt.AdfDiagram.prototype.getSearchPanel = function(bCreate) {
  if (bCreate && !this._searchPanel) {
    this._searchPanel = this.CreateSearchPanel();
  }
  return this._searchPanel;
};


/**
 * @protected
 * Creates search panel
 * @return {dvt.SearchPanel} search panel for the component
 */
dvt.AdfDiagram.prototype.CreateSearchPanel = function() {
  var panel = new dvt.SearchPanel(this.getCtx(), this.HandleSearchEvent, this);
  panel.setSkinName(this.getSkinName());
  panel.setResultsMaxWidth(.75 * this.getWidth());
  panel.setResultsMaxHeight(.75 * this.getHeight());
  this.EventManager.addComponentKeyboardHandler(panel);
  return panel;
};

dvt.AdfDiagram.prototype.HandleSearchEvent = function(event) {
  //do nothing for now, let subclasses override
  this.dispatchEvent(event);
};

/**
 * Enable search keyboard handler
 */
dvt.AdfDiagram.prototype.enableSearchKeyboardHandler = function() {
  //When a mouse operation is performed on dvt.AdfDiagram, diagram becomes the current keyboard handler
  //Any subsequent keyboard operation to perform search is ignored, because all the search keyboard events gets directed to Diagram.
  //Following workaround will set the current keyboard handler as search panel
  //TO DO: Bug 18792535  Need to replace the keyboard navigation workaround
  this.EventManager.setCurrentKeyboardHandler(this.getSearchPanel());
};


/**
 * Enable diagram keyboard handler
 */
dvt.AdfDiagram.prototype.enableDiagramKeyboardHandler = function() {
  //give control to the diagram event manager - it might be needed in situations when search panel was handling events,
  //then it was collapsed
  this.EventManager.setCurrentKeyboardHandler(this.EventManager);
};

dvt.AdfDiagram.prototype.LoadSearchResultsData = function(xNode, searchText, searchType, clientRowKey) {
  var searchPanel = this.getSearchPanel();
  if (searchPanel) {
    var xmlNode = xNode;
    //walk down to <data>
    while (xmlNode && xmlNode.getName() != dvt.SearchPanel.ELEM_SEARCH_RESULTS_DATA) {
      xmlNode = xmlNode.getFirstChild();
    }

    if (xmlNode) {
      searchPanel.parseResultsData(xmlNode, searchText, searchType, clientRowKey);
    }
  }
};


/**
 * Load search results JSON data
 * @param {object} jsonData  JSON search results data
 * @param {String} searchText  Search text
 * @param {String} searchType  Search Type
 * @param {String} clientRowKey  If no client row key is specified, then it is initial list
 * @protected
 */
dvt.AdfDiagram.prototype.LoadSearchResultsDataJson = function(jsonData, searchText, searchType, clientRowKey) {
  var searchPanel = this.getSearchPanel();
  if (searchPanel) {
    var jsonObj = jsonData[dvt.SearchPanel.ELEM_SEARCH_RESULTS_DATA];
    if (jsonObj) {
      searchPanel.parseResultsDataJson(jsonObj, searchText, searchType, clientRowKey);
    }
  }
};

/**
 * Renders search panel if it is necessary
 */
dvt.AdfDiagram.prototype.RenderSearchPanel = function() {
  var searchPanel = this.getSearchPanel();

  if (searchPanel && !searchPanel.canReuse()) {
    this.addChild(searchPanel);
    searchPanel.renderComponent(dvt.Bundle.getTranslatedString(dvt.Bundle.SUBCOMPONENT_PREFIX, 'SEARCH'), this.getResourcesMap());
  }
  this.SetSearchPanel(searchPanel);
};

dvt.AdfDiagram.prototype.SetSearchPanel = function(panel) {
  this._searchPanel = panel;
  this.PositionSearchPanel();
};

dvt.AdfDiagram.prototype.RemoveSearchPanel = function() {
  if (this._searchPanel) {
    if (this._searchPanel.getParent()) {
      this._searchPanel.getParent().removeChild(this._searchPanel);
    }
    if (this._searchPanel.destroy) {
      this._searchPanel.destroy();
    }
    this._searchPanel = null;
  }
};

dvt.AdfDiagram.prototype.PositionSearchPanel = function() {
  var transX;
  if (this._searchPanel && !this.UsePanelDrawer()) {
    if (!dvt.Agent.isRightToLeft(this.getCtx())) {
      transX = this.getWidth() - dvt.AdfDiagram._SEARCH_PANEL_POS_PADDING - this._searchPanel.getWidth();
    }
    //BiDi: position search panel on the left side of the view
    else {
      transX = dvt.AdfDiagram._SEARCH_PANEL_POS_PADDING;
    }
    this._searchPanel.setTranslate(transX, dvt.AdfDiagram._SEARCH_PANEL_POS_PADDING);
  }
};

dvt.AdfDiagram.prototype.CreateClientBehaviorHandler = function(context, callback, callbackObj) {
  return new DvtAdfDiagramClientBehaviorHandler(context, callback, callbackObj);
};

dvt.AdfDiagram.prototype.setOverviewWindowContentStyle = function(ovContentStyle) {
  this._ovContentStyle = ovContentStyle;
};

dvt.AdfDiagram.prototype.getOverviewWindowContentStyle = function() {
  return this._ovContentStyle;
};


/**
 * Sets the focus on the search field for keyboard
 * @protected
 */
dvt.AdfDiagram.prototype.FocusSearchField = function() {
  if (this._searchPanel) {
    var searchPanel = this._searchPanel;
    var onEnd = function() {
      searchPanel.focusTextField();
    };
    if (this.UsePanelDrawer()) {
      if (this._panelDrawer.isDisclosed()) {
        if (this._panelDrawer.getDisplayedPanelId() == dvt.PanelDrawer.PANEL_SEARCH) {
          this._searchPanel.focusTextField();
        }
        else {
          this._panelDrawer.setDisplayedPanelId(dvt.PanelDrawer.PANEL_SEARCH, false, onEnd);
        }
      }
      else {
        this._panelDrawer.setDisplayedPanelId(dvt.PanelDrawer.PANEL_SEARCH);
        this._panelDrawer.setDisclosed(true, false, onEnd);
      }
    }
    else if (this._searchPanel.getState() == dvt.SearchPanel.STATE_COLLAPSED) {
      this._searchPanel.toggleExpandCollapse(onEnd);
    }
    else if (this._searchPanel.getState() == dvt.SearchPanel.STATE_EXPANDED) {
      this._searchPanel.focusTextField();
    }
  }
};

/**
 * Check if search panel is disclosed
 * @return {boolean} true if search panel is disclosed
 */
dvt.AdfDiagram.prototype.isSearchPanelDisclosed = function() {
  if (this._searchPanel) {
    //if the search panel is embedded in panel drawer, check if the panel drawer is disclosed
    //if the search panel isn't embedded in panel drawer, check if the search panel is in expanded state
    if ((this.UsePanelDrawer() && this._panelDrawer.isDisclosed()
              && this._panelDrawer.getDisplayedPanelId() == dvt.PanelDrawer.PANEL_SEARCH)
         || (!this.UsePanelDrawer() && this._searchPanel.getState() == dvt.SearchPanel.STATE_EXPANDED)) {

      return true;
    }
  }

  return false;
};

/**
 * Collapse search panel
 */
dvt.AdfDiagram.prototype.collapseSearchPanel = function() {
  if (this.isSearchPanelDisclosed()) {
    if (this.UsePanelDrawer())
      this._panelDrawer.setDisclosed(false, false); //Collapse the panel drawer containing search panel
    else
      this._searchPanel.toggleExpandCollapse(); //Collapse the search panel
  }
};

/**
 * Gets node-related style attributes
 * @return {object} a map containing node-related style attributes (keys defined in dvt.AdfDiagramNode)
 */
dvt.AdfDiagram.prototype.getNodeStyles = function() {
  return this._nodeStyles;
};


/**
 * Sets node-related style attributes
 * @param {object} nodeStyles a map containing node-related style attributes (keys defined in dvt.AdfDiagramNode)
 */
dvt.AdfDiagram.prototype.setNodeStyles = function(nodeStyles) {
  this._nodeStyles = nodeStyles;
};


/**
 * Gets link-related style attributes
 * @return {object} a map containing link-related style attributes (keys defined in dvt.AdfDiagramLink)
 */
dvt.AdfDiagram.prototype.getLinkStyles = function() {
  return this._linkStyles;
};


/**
 * Sets link-related style attributes
 * @param {object} linkStyles a map containing link-related style attributes (keys defined in dvt.AdfDiagramLink)
 */
dvt.AdfDiagram.prototype.setLinkStyles = function(linkStyles) {
  this._linkStyles = linkStyles;
};

dvt.AdfDiagram.prototype.getBreadcrumbsStyles = function(state) {
  if (this._breadcrumbsStyles && this._breadcrumbsStyles[state])
    return this._breadcrumbsStyles[state];
  else
    return null;
};

dvt.AdfDiagram.prototype.setBreadcrumbsStyles = function(breadcrumbsStyles) {
  this._breadcrumbsStyles = breadcrumbsStyles;
};


/**
 * Gets a map of editor-related styles
 * @return {object} the map of editor-related styles
 */
dvt.AdfDiagram.prototype.getEditorStyles = function() {
  if (!this._editorStyleMap) {
    var editorStyleMap = new Object();

    //node states : ena/ovr
    editorStyleMap[DvtAdfDiagramEndpointUtils.ENDPOINT_NODE_BORDER_COLOR_ATTR_ENA] = this.getNodeStyles()[dvt.AdfDiagramNode.HOVER_OUTER_COLOR];
    editorStyleMap[DvtAdfDiagramEndpointUtils.ENDPOINT_NODE_BG_COLOR_ATTR_ENA] = '#FFFFFF';

    //node states : dwn
    editorStyleMap[DvtAdfDiagramEndpointUtils.ENDPOINT_NODE_BORDER_COLOR_ATTR_DWN] = this.getNodeStyles()[dvt.AdfDiagramNode.SELECTED_COLOR];
    editorStyleMap[DvtAdfDiagramEndpointUtils.ENDPOINT_NODE_BG_COLOR_ATTR_DWN] = '#FFFFFF';

    //link states : ena/ovr/dwn
    editorStyleMap[DvtAdfDiagramEndpointUtils.ENDPOINT_LINK_BORDER_COLOR_ATTR] = this.getLinkStyles()[dvt.AdfDiagramLink.SELECTED_COLOR];
    editorStyleMap[DvtAdfDiagramEndpointUtils.ENDPOINT_LINK_BG_COLOR_ATTR] = '#FFFFFF';
    this._editorStyleMap = editorStyleMap;
  }
  return this._editorStyleMap;
};


/**
 * Create Af Context
 * @public
 * @param {number} nodeId node id
 * @return {dvt.AfContext} dvt.AfContext object instance
 */
dvt.AdfDiagram.prototype.createAfContext = function(nodeId) {
  var afContext = new dvt.AfContext(this.getCtx(), this.getEventManager());
  afContext.setSkinName(this._skinName);

  //Bug 16717528 - diagram nodes overlapped if images are not loaded
  //Bug 13536792 - images without explicit size fail to render
  afContext.setImageCallback(this, this.onAfImageLoaded, this.addToAfImageNotReadyList);

  //set menu dropdown container
  afContext.setMenuDropdownContainer(this);

  if (nodeId) {
    var node = this.getNodeById(nodeId);
    if (node)
      afContext.setELContext(node.getData().getElAttributes());
  }

  return afContext;
};

/*-------------------------------------------------------------------------*/
/*   PanelCard                                                             */
/*-------------------------------------------------------------------------*/


/**
 * handle panelCard events
 * @param {String} eventType dvt.AfPanelCard.SET_PROP_EVENT or dvt.AfPanelCard.FETCH_DATA_EVENT
 */
dvt.AdfDiagram.prototype.panelCardEventCallback = function(eventType, nodeId, index) {
  //TODO: are we in a hover window??
  //TODO: don't fire any events for now

  if (dvt.AfPanelCard.SET_PROP_EVENT == eventType) {
    if (! this._bBlockCardPropEvent)
      this.fireCardPropEvent(nodeId, index);
  }
  else if (dvt.AfPanelCard.FETCH_DATA_EVENT == eventType) {
    if (! this._bBlockFetchCardEvent) {
      this.fireFetchCardEvent(nodeId, index,
                              this._detailNodeContents ? true : false);
    }
  }

  this.HideNodeTransientToolbar();
};


// add entry to cardIndexes Map
// In Sync all: nodeId is SYNC_PANEL_CARDS_KEY, detailItem is a detailItem text or icon
// In latNav: nodeId is a list of nodes, detailItem is a list of indexes in a String
// otherwise: nodeId is a node and detailItem is a index (number)
// parmValues contains nodeId1$afr$index1$afr$nodeId2$afr$index2
// syncPanelCard   syncCards$afr$Item 2
dvt.AdfDiagram.prototype.fireCardPropEvent = function(nodeId, detailItem, bHoverWindow) {
  //dont fire CardPropEvent for the following conditions
  //1. if caller is syncPanelCards

  //2. if caller is walkTreeFetchCardIfNeeded
  //because the list was built from this._cardIndexes

  //3. if caller is addPanelCarddata
  //and if the last entry is sync all and syncAll index = specified detailItem

  if (this._bBlockCardPropEvent)
    return;

  // if syncPanelCards, remove all entries except panelCard that don't have detailItem
  // nodeId is SYNC_PANEL_CARDS_KEY and detailItem is a list of String
  if (nodeId == dvt.AdfDiagram.SYNC_PANEL_CARDS_KEY) {
    this._cardIndexes = this.cleanupPanelCardList(detailItem);

    // add to the cardIndexes Map
    this._cardIndexes.push(new Array(nodeId, detailItem));
  }

  // remove the old one if exists
  // only one node and one index in number

  //TODO: typeof(detailItem) == "number" ???
  else if (! isNaN(detailItem)) {
    //if this is hover window, don't fire card prop change event
    if (bHoverWindow)
      return;

    //Bug 9739470 - hv panel cards: for flash hv, the panel card state is not updated
    this._cardIndexes = this.removeFromPanelCardList(nodeId);

    // add to the cardIndexes Map
    this._cardIndexes.push(new Array(nodeId, detailItem));
  }

  // both nodeId and detailItem are a list of String
  else {
    var nodeIdList = nodeId.split(dvt.AdfDiagram.ROWKEY_DELIMITER);
    var indexList = detailItem.split(dvt.AdfDiagram.ROWKEY_DELIMITER);
    if (nodeIdList.length == indexList.length) {
      for (var i = 0; i < nodeIdList.length; i++) {
        this._cardIndexes = this.removeFromPanelCardList(nodeIdList[i]);
        this._cardIndexes.push(new Array(nodeIdList[i], parseInt(indexList[i])));
      }
    }
  }

  //dumpCardList();

  this._fireCardPropEvent();
};


dvt.AdfDiagram.prototype._fireCardPropEvent = function() {
  var params = {};
  params['cardIndexes'] = this.convertPanelCardListToString(this._cardIndexes);

  var event = new dvt.PanelCardEvent(dvt.PanelCardEvent.PANELCARD_INDEX_EVENT, params);

  this.dispatchEvent(event);
};

//defer panelCard loading
// add entry to cardIndexes Map
// parmValues contains nodeId1$afr$index1$afr$nodeId2$afr$index2
// syncPanelCard   syncCards$afr$Item 2

dvt.AdfDiagram.prototype.fireFetchCardEvent = function(nodeId, sdiIndex, bHoverWindow) {
  this.fireCardPropEvent(nodeId, sdiIndex, bHoverWindow);

  if (this._bBlockFetchCardEvent)
    return;

  var nodeIdList =
      this.getNodesNeedFetchPanelCard(nodeId, sdiIndex);

  //fire fetch panelCard event to server
  if (nodeIdList) {
    var params = {};
    params['clientRowKey'] = nodeIdList;
    params['nextc'] = sdiIndex;

    //BUG FIX #8930021: send back flag indicating whether this is the hover window
    //(use strings instead of booleans because framework seems to have problem
    //sending booleans back and forth)
    params['pchw'] = bHoverWindow;

    var event = new dvt.PanelCardEvent(dvt.PanelCardEvent.PANELCARD_FETCH_EVENT, params);

    //trace("fetching cards..." + nodeIdList + ", " + sdiIndex);
    this.dispatchEvent(event);
  }
};

/**
 * Remove all entries except panelCard that DONOT have the specified detailItem
 * @param {number} sdiIndex index of a visible showDetailItem in a panelCard
 * @return {boolean} returns true if panel card is cleaned up
 */
dvt.AdfDiagram.prototype.cleanupPanelCardList = function(sdiIndex) {
  var thisRef = this;

  return this._cardIndexes.filter(
      function(item, index, arr)
      {
        var entry = item;
        if (entry && entry.length == 2) {
          if (entry[0] != dvt.AdfDiagram.SYNC_PANEL_CARDS_KEY) {
            var node = thisRef.getNodeById(entry[0]);
            if (thisRef._findPanelCardIndex(node, sdiIndex) == -1) {
              return true;
            }
          }
        }
        return false;
      }
  );
};

// remove the item from cardIndexes Array if exists
dvt.AdfDiagram.prototype.removeFromPanelCardList = function(nodeId) {
  return this._cardIndexes.filter(
      function(item, index, arr)
      {
        var entry = item;
        if (entry && entry.length == 2) {
          return (entry[0] != nodeId);
        }
        return false;
      }
  );
};

/**
 * @private
 * Get the index of a visible showDetailItem in a panelCard with the text/icon matches
 * @param {dvt.AdfDiagramNode} node  Diagram node
 * @param {String} sdiText  showDetailItem text
 * @return {Number} index of a visible showDetailItem in a panelCard with the text/icon matches
 */
dvt.AdfDiagram.prototype._findPanelCardIndex = function(node, sdiText) {
  if (node) {
    var panelCard = this._getFirstPanelCard(node);
    if (panelCard) {
      return panelCard.getVisibleDetailItemIndex(sdiText,
                                                 node.getData());
    }
  }
  return -1;
};

/**
 * @private
 * Get the first panel card of the given node at zoom 100 facet
 *
 * @param {dvt.AdfDiagramNode} node  Node to get the panel card from
 * @return {dvt.AfPanelCard} first panel card of the node at zoom 100 facet
 */
dvt.AdfDiagram.prototype._getFirstPanelCard = function(node) {
  var panelCard = this._getFirstPanelCardTemplate(node);
  //stamp the zoom100 panel card
  if (panelCard) {
    panelCard = panelCard.stamp(node.getData().getElAttributes());
  }
  return panelCard;
};

/**
 * @private
 * Get the panelCardTemplate with the given node
 *
 * @param {dvt.AdfDiagramNode} node  Node to get the panel card from
 * @return {dvt.AfPanelCard} panel card specified by the given node id
 */
dvt.AdfDiagram.prototype._getFirstPanelCardTemplate = function(node) {
  if (! node)
    return null;

  var oldZoom = node.getZoom();
  node.setForceZoom100Facet(true);
  var template = node.GetNodeTemplate();

  //create an array to store templates for the breadth-first search
  var templates = [];
  templates.push(template);

  var panelCard;
  var removedElem;
  while (templates.length > 0) {
    //remove the first template from the array
    removedElem = templates.splice(0, 1);
    template = removedElem[0];

    //if the component is an dvt.AfPanelCard, we're done
    if (template instanceof dvt.AfPanelCard) {
      panelCard = template;
      break;
    }

    //if no panel card yet, add all the children of
    //this template to the end of the array
    var numChildTemplates = template.getNumChildren();
    for (var i = 0; i < numChildTemplates; i++)
      templates.push(template.getChildAt(i));
  }

  //Fix Bug 22121707 - hv: zoom facet doesn't change when zoom in
  //If Zoom100 is used just to get the template and not to process the template, retain the zoom level
  //Because if the zoom level changes to 100% (by hitting NUM 5), old and new zoom levels will be same and
  //dvt.AdfDiagramNode.GetNodeTemplate will assume there is no change in zoom level/template and will return null.
  //Zoom100 facet is used here to process the panel card template and not the node template
  node.setForceZoom100Facet(false);
  node.setZoom(oldZoom);
  return panelCard;
};


dvt.AdfDiagram.prototype.convertPanelCardListToString = function(panelCardList) {
  var s = null;
  if (panelCardList) {
    for (var i = 0; i < panelCardList.length; i++) {
      var entry = panelCardList[i];
      if (entry && entry.length == 2) {
        if (s) {
          s = s + dvt.AdfDiagram.ROWKEY_DELIMITER + entry[0] +
              dvt.AdfDiagram.ROWKEY_DELIMITER + entry[1];
        }
        else {
          s = entry[0] + dvt.AdfDiagram.ROWKEY_DELIMITER + entry[1];
        }
      }
    }
  }
  return s;
};


//defer panelCard loading
//  check all visible nodes to see if panelCard content need to fetch from server
//  return a list of clientRowKeys that needs data from server
//  using ROWKEY_DELIMITER as separator
//  if all panelCard are loaded, return null
dvt.AdfDiagram.prototype.getNodesNeedFetchPanelCard = function(nodeId, sdiIndex) {

  // sync all panelCard
  if (nodeId == dvt.AdfDiagram.SYNC_PANEL_CARDS_KEY) {
    var node;
    var itemText = sdiIndex;
    var nodeIdList = '';
    var thisRef = this;

    // fetch panelCards for visible nodes only
    this.VisitVisibleNodes(
        function(node) {
          nodeIdList = thisRef._getNodesNeedFetchPanelCard(node, itemText, -1, nodeIdList);
        },
        this
    );

    if (nodeIdList.length == 0)
      return null;

    return nodeIdList;
  }

  // fetch content for only one panelCard (not sync all)
  else {
    return nodeId;
  }
};

/**
 * @private
 * Get the list of node ids to fetch panel card
 * @param {dvt.AdfDiagramNode} node  Diagram Node
 * @param {String} detailItem  show detail item text
 * @param {Number} itemIndex  Item index
 * @param {String} nodeIdList  Existing list of node ids that needs to fetch panel card
 * @return {String}  List of node ids that needs to fetch panel card
 */
dvt.AdfDiagram.prototype._getNodesNeedFetchPanelCard = function(
    node, detailItem, itemIndex, nodeIdList) {

  var nodeId = node.getData().getId();

  //Note: only look at contentDelivery of panelCard at zoom 100
  var panelCard = this._getFirstPanelCard(node);

  if (panelCard && panelCard.isPanelCardDeliveryLazy()) {
    if (itemIndex == -1)
      itemIndex = panelCard.getVisibleDetailItemIndex(detailItem);

    //check if the card is cached?
    if (itemIndex != -1 && ! this.isShowDetailItemLoaded(nodeId, itemIndex)) {
      if (nodeIdList.length > 0)
        nodeIdList += dvt.AdfDiagram.ROWKEY_DELIMITER;
      nodeIdList += nodeId;
    }
  }
  return nodeIdList;
};


/**
 * @protected
 * Walk all visible nodes
 * @param {function} func function to call
 * @param {object} funcObj function param object
 */
dvt.AdfDiagram.prototype.VisitVisibleNodes = function(func, funcObj) {
  for (var nodeId in this._nodes) {
    var node = this.getNodeById(nodeId);

    if (node.getVisible()) {
      func.call(funcObj, node);
    }
  }
};


dvt.AdfDiagram.prototype.startParsingPanelCardData = function() {
  this._newNodes = [];
};

/**
 * Add panel card data to the given node
 * @param {number} nodeId  Node Id of the node
 * @param {Array} attributes  List of EL attributes
 */
dvt.AdfDiagram.prototype.addPanelCardData = function(nodeId, attributes) {
  var node = this.getNodeById(nodeId);
  if (node) {
    node.getData().addElAttributes(attributes);
    this._newNodes.push(node);
  }
};


/**
 * Add Panel Card JSON Data
 * @param {number} nodeId  Node Id of the node
 * @param {Array} attributes  List of EL attributes
 */
dvt.AdfDiagram.prototype.addPanelCardDataJson = function(nodeId, attributes) {
  var node = this.getNodeById(nodeId);
  if (node) {
    node.getData().addElAttributesJson(attributes);
    this._newNodes.push(node);
  }
};

dvt.AdfDiagram.prototype.stopParsingPanelCardData = function() {
  this._newNodes = null;
};


/**
 * Add data to the PanelCard.
 * @param {string} nodeIdList  node ids
 * @param {string} sdiIndex sdi index
 * @param {string} hoverWindow  flag indicates if this is for hover window
 */
dvt.AdfDiagram.prototype.applyPanelCardData = function(nodeIdList, sdiIndex, hoverWindow) {
  //TODO:use diff flag??
  //  this._bRendered = false;

  try {
    //TODO
    //    this.createUpdateTransitioner();

    //process the additional data in the context of the event that triggered it
    //TODO
    //    this.processEventContext(eventType, paramKeys, paramValues, this.update());

    //in case of SyncAll or latNav to new nodes, nodeIdList is a list of node ids
    //otherwise it's a nodeId
    var nodeIdList = nodeIdList.split(dvt.AdfDiagram.ROWKEY_DELIMITER);
    var indexList = null;
    var bSwitchOneCard;
    var bSyncAll = false;

    if (isNaN(sdiIndex)) {
      indexList = sdiIndex.split(dvt.AdfDiagram.ROWKEY_DELIMITER);

      // In sync all, block all panelCard prop change events
      if (indexList.length == 1) {
        bSyncAll = true;
        this._bBlockCardPropEvent = true;
      }
    }
    //switch one card
    else {
      bSwitchOneCard = true;
    }

    //block firing panelCard event during process fetchPanelCardEvent
    this._bBlockFetchCardEvent = true;

    var bHoverWindow = (hoverWindow == 'true');

    //loop over each id and add it to the panelCard sdiIndexes map
    for (var i = 0; i < nodeIdList.length; i++) {

      var nodeId = nodeIdList[i];
      var node = this.getNodeById(nodeId);

      //BUG FIX #8930021: if this is for the hover window, then get that
      //node instead of the real one
      //Note: getDetailContents only returns the right thing in HV
      if (bHoverWindow && node.getDetailContents()) {
        node = node.getDetailContents();
      }

      //display the next showDetailItem if the displayObj exists
      //otherwise, save index in stateProp
      var displayObj = this.getPanelCard(node);
      if (displayObj) {
        var nextIndex = 0;

        var saveBlockCardPropEvent = this._bBlockCardPropEvent;
        var saveBlockFetchCardEvent = this._bBlockFetchCardEvent;

        if (bSwitchOneCard) {
          //TODO: if sdiIndex is a number, will parseInt work in FF and IE?
          nextIndex = parseInt(sdiIndex);
        }
        else if (bSyncAll) {
          var pcTemplate = displayObj;
          if (pcTemplate)
            nextIndex = pcTemplate.getVisibleDetailItemIndex(sdiIndex,
                                                             node.getData());
        }
        else {
          nextIndex = parseInt(indexList[i]);
        }

        //Bug 13723923 - wrong showdetailitem in panelcard after browser refresh
        //don't fire additional card prop event
        if (! bSyncAll) {
          this._bBlockFetchCardEvent = true;

          //dont fire card prop change event for the following conditions:
          var val = this.getPanelCardState(nodeId);
          if (val && (nextIndex == val)) {
            this._bBlockCardPropEvent = true;
          }
          else {
            val = this.getPanelCardState(dvt.AdfDiagram.SYNC_PANEL_CARDS_KEY);
            if (val && nextIndex == this._findPanelCardIndex(node, val)) {
              this._bBlockCardPropEvent = true;
            }
          }
        }

        this.setShowDetailItemLoaded(nodeId, nextIndex);
        displayObj.displayDetailItem(nextIndex);

        this._bBlockCardPropEvent = saveBlockCardPropEvent;
        this._bBlockFetchCardEvent = saveBlockFetchCardEvent;

      }
      //display object is not created yet
      else {
        node.setStateProp(dvt.AfPanelCard.DISPLAYED_DETAIL_ITEM_INDEX_KEY, nextIndex);
      }
    }
    this._bBlockCardPropEvent = false;
    this._bBlockFetchCardEvent = false;

  }
  catch (error) {
    var sTrace = error.type;
  }

};


/**
 * Get the AfPanelCard with the given id from the node, or the first
 * panel card if no id is specified.
 *
 * @param node node to get the panel card from
 * @param panelCardId optional id to specify which panel card to get;
 *        if null, the first panel card will be returned
 *
 * @return panel card specified by the given id, or the first one if no id is specified
 */
dvt.AdfDiagram.prototype.getPanelCard = function(node) {
  if (! node)
    return null;

  var contentRoot = node.GetAfComponentRoot();
  if (! contentRoot)
    return null;

  //create an array to store the components for the breadth-first search
  var ndoArray = new Array();
  ndoArray.push(contentRoot);

  var removedElem;
  while (ndoArray.length > 0) {
    //remove the first item from the array
    removedElem = ndoArray.splice(0, 1);
    contentRoot = removedElem[0];

    if (contentRoot instanceof dvt.AfPanelCard) {
      return contentRoot;
    }

    //if no panel card yet, add the AfComponentBase children
    //of this component to the end of the array
    for (var i = 0; i < contentRoot.getNumChildren(); i++) {
      var child = contentRoot.getChildAt(i);
      if (child instanceof dvt.AfComponent)
        ndoArray.push(child);
    }
  }

  return null;
};


// nodeId: either nodeId or SYNC_PANEL_CARDS_KEY
//
// This method returns:
// null if no panelCard state for this node
// index: if this node has a panelCard set explicitly
// text or icon string: sync all
dvt.AdfDiagram.prototype.getPanelCardState = function(nodeId) {
  if (this._cardIndexes) {
    for (var index = this._cardIndexes.length - 1; index >= 0; index--) {
      var entry = this._cardIndexes[index];
      if (entry && entry.length == 2 && entry[0] == nodeId) {
        return entry[1];
      }
    }
  }
  return null;
};

/**
 * Get panel card index for the given node id
 * @param {string} nodeId  node id
 * @return {number} index of a visible showDetailItem in a panelCard
 */
dvt.AdfDiagram.prototype.getPanelCardIndex = function(nodeId) {
  var state = this.getPanelCardState(nodeId);
  if (state) {
    // sync all
    if (isNaN(state)) {
      return this._findPanelCardIndex(this.getNodeById(nodeId), state);
    }
    return state;
  }
  return null;
};

//is the showDetailItem locally available?
dvt.AdfDiagram.prototype.isShowDetailItemLoaded = function(nodeId, sdiIndex) {
  //Note: return true if not lazy panelCard content delivery

  var ret = true;
  if (this._nodeAndLoadedCardsMap[nodeId]) {
    ret = this._nodeAndLoadedCardsMap[nodeId][sdiIndex];
  }

  //trace("is Node loaded: " + nodeId + " " + ret);
  //NOTE: if panelCard content delivery is lazy, the array of indexes should not be null
  return ret;
};

/**
 * Sets the show detail item loaded flag for the given node id and sdi index
 * NOTE: this method is called only when panelCard content is lazy delivery
 * @param {string} nodeId  node id
 * @param {number} sdiIndex  index of a visible showDetailItem in a panelCard
 */
dvt.AdfDiagram.prototype.setShowDetailItemLoaded = function(nodeId, sdiIndex) {
  var node = this.getNodeById(nodeId);
  if (node) {
    //Note: only look at contentDelivery of panelCard at zoom 100
    var panelCard = this._getFirstPanelCardTemplate(node);

    //Note: only add to this._nodeAndLoadedCardsMap when it is lazy
    if (! panelCard || (panelCard && panelCard.isPanelCardDeliveryLazy())) {

      if (! this._nodeAndLoadedCardsMap[nodeId]) {
        this._nodeAndLoadedCardsMap[nodeId] = [];
      }
      this._nodeAndLoadedCardsMap[nodeId][sdiIndex] = true;
    }
  }
};


/**
 * clear a specifed state from all cached node sprite
 */
dvt.AdfDiagram.prototype._clearStateFromCache = function(key) {
  for (var id in this._nodes) {
    var n = this._nodes[id];
    if (n) {
      n.setStateProp(key, null);
    }
  }
};

// set showDetailItemIndex to the stateProp of node
dvt.AdfDiagram.prototype._setPanelCardState = function(node,
    detailItemText,
    detailItemIndexKey) {

  var index = this._findPanelCardIndex(node, detailItemText);
  if (index != -1) {
    if (! detailItemIndexKey)
      detailItemIndexKey = dvt.AfPanelCard.DISPLAYED_DETAIL_ITEM_INDEX_KEY;
    node.setStateProp(detailItemIndexKey, index);
  }
};


/**
  * Set the panelCard detailItemIndexes from the server
  * @param {string} s  list of (nodeIds, DetailItemIndexes) delimited by ROWKEY_DELIMITER
  */
dvt.AdfDiagram.prototype.setCardIndexes = function(s) {
  //defer panelCard loading
  if (s == null)
    return;

  var id;
  var a = s.split(dvt.AdfDiagram.ROWKEY_DELIMITER);

  // do nothing if odd number
  if (a.length == 0 || (a.length & 1))
    return;

  // clear all detailItem state
  var detailItemIndexKey = dvt.AfPanelCard.DISPLAYED_DETAIL_ITEM_INDEX_KEY;
  this._clearStateFromCache(detailItemIndexKey);

  //loop over each id and add it to the panelCard detailItemIndexes map
  for (var i = 0; i < a.length; i += 2) {
    var node;
    // sync all panelCards
    if (a[i] == dvt.AdfDiagram.SYNC_PANEL_CARDS_KEY) {
      node = null;
      // sync all panelCards that are currently displayed
      for (id in this._nodes) {
        var n = this._nodes[id];
        // dont override if detailItem is already specified
        if (n == null || n.getStateProp(detailItemIndexKey) != null)
          continue;
        this._setPanelCardState(n, a[i + 1], detailItemIndexKey);
      }
      // add to panelCard indexes array
      this._cardIndexes.push(new Array(a[i], a[i + 1]));
    }
    else {
      node = this.getNodeById(a[i]);

      // add to panelCard indexes array. Note that index is an int
      this._cardIndexes.push(new Array(a[i], parseInt(a[i + 1])));
    }

    // add to node state prop if nodes exist
    if (node && node.setStateProp) {
      node.setStateProp(detailItemIndexKey, a[i + 1]);
    }
  }
};


//nodeShowDetailItems="3$afr$0$afr$2$afr$0$afr$1$afr$0$afr$0$afr$0$afr$4$afr$0"
//ex: nodeId=3,2,1,0,4 showDetailItemIndex=0
dvt.AdfDiagram.prototype.setNodeShowDetailItemIndexMap = function(s) {
  var arr = s.split(dvt.AdfDiagram.ROWKEY_DELIMITER);
  // do nothing if odd number
  if (arr.length == 0 || (arr.length & 1))
    return;

  //loop over each id and add it to the panelCard detailItemIndexes map
  for (var i = 0; i < arr.length; i += 2) {
    this.setShowDetailItemLoaded(arr[i], parseInt(arr[i + 1]));
  }
};

/**
 * Set the sdi loaded state in the node
 * @param {string} s  list of (nodeIds, DetailItemIndexes) delimited by ROWKEY_DELIMITER
 */
dvt.AdfDiagram.prototype.setShowDetailItems = function(s) {
  //Bug 14534257 - HTML5: panelCard of expanded child node didn't sync to parent
  //newNodeSdis="3$afr$0$afr$2$afr$0$afr$1$afr$0$afr$0$afr$0$afr$4$afr$0"
  //ex: nodeId=3,2,1,0,4 showDetailItemIndex=0
  var arr = s.split(dvt.AdfDiagram.ROWKEY_DELIMITER);
  // do nothing if odd number
  if (arr.length == 0 || (arr.length & 1))
    return;

  //loop over each id and add it to the panelCard detailItemIndexes map
  for (var i = 0; i < arr.length; i += 2) {
    var node = this.getNodeById(arr[i]);
    var sdiIndex = parseInt(arr[i + 1]);
    if (sdiIndex > 0) {
      node.setStateProp(dvt.AfPanelCard.DISPLAYED_DETAIL_ITEM_INDEX_KEY, sdiIndex);
    }
  }
};

/**
 * Set Panel Card Navigation button style
 * @param {String} cardNavButtonSkin  Panel Card Navigation button style
 */
dvt.AdfDiagram.prototype.setCardNavButtonSkin = function(cardNavButtonSkin) {
  this._cardNavButtonSkin = cardNavButtonSkin;
};

/**
 * Get Panel Card Navigation button style
 * @return {String} Panel Card Navigation button style
 */
dvt.AdfDiagram.prototype.getCardNavButtonSkin = function() {
  return this._cardNavButtonSkin;
};

dvt.AdfDiagram.prototype.getCardSelectorList = function() {
  return this._cardSelectorList;
};

dvt.AdfDiagram.prototype.setCardSelectorList = function(cardList) {
  //nothing to update
  if (! cardList)
    return;

  var needUpdate = false;

  //initial case, selector dropdown will be created when control panel is expanded
  if (this._cardSelectorList == null) {
    this._cardSelectorList = cardList;
  }

  // is there any new item in the cardList?
  else {
    var newL;
    var oldL;
    for (var i = 0; i < cardList.length; i++) {
      newL = cardList[i];

      //already exist?
      var found = false;
      for (var j = 0; j < this._cardSelectorList.length; j++) {
        oldL = this._cardSelectorList[j];
        if (oldL[0] == newL[0] && oldL[1] == newL[1] && oldL[2] == newL[2]) {
          found = true;
          break;
        }
      }
      if (! found) {
        needUpdate = true;
        break;
      }
    }
  }

  // update panelCard selector dropdown list
  if (needUpdate) {
    this._cardSelectorList = cardList;
    if (this._controlPanel) {
      this._controlPanel.updateCardSelectorList();
    }
  }
};


/**
 * Sync all panel cards with the given id to show the specified
 * detail item.
 *
 * @param panelCardId id of the panelCard to sync
 * @param sdiId id of the detail item to show in the panel cards
 * @param sdiText text of the detail item to show in the panel cards
 * @param sdiIcon icon URL of the detail item to show in the panel cards
 */
dvt.AdfDiagram.prototype.syncPanelCards = function(sdiId, sdiText, sdiIcon) {

  var syncPanelCardFunc = function(node) {
    this.syncOnePanelCard(node, sdiId, sdiText, sdiIcon);
    return false;
  };

  // if sdiText is not specified use sdiIcon
  if (! sdiText)
    sdiText = sdiIcon;

  // fire sync panelCards prop event
  //defer panelCard loading
  //BUG FIX #8930021: if doing sync, then this is not the hover window

  this.fireFetchCardEvent(dvt.AdfDiagram.SYNC_PANEL_CARDS_KEY, sdiText, false);

  this._bBlockCardPropEvent = true;
  this._bBlockFetchCardEvent = true;

  // if node is visible, display it
  // otherwise setStateProp
  this.VisitNodes(syncPanelCardFunc, this);

  this._bBlockCardPropEvent = false;
  this._bBlockFetchCardEvent = false;
};

/**
 * Syncs the panel card in the node
 * @param {dvt.AdfDiagramNode} beginNode  the node to sync the panel cards
 * @param {Number} sdiId  the id of show detail item
 * @param {String} sdiText  the text of show detail item
 * @param {String} sdiIcon  the icon URL of show detail item
 */
dvt.AdfDiagram.prototype.syncOnePanelCard = function(
    beginNode, sdiId, sdiText, sdiIcon) {

  var node = beginNode;
  var nodeData = node.getData();
  var panelCard;
  var key = dvt.AfPanelCard.DISPLAYED_DETAIL_ITEM_INDEX_KEY;
  var numSdis;
  var j;
  var sdiTemp;

  //flag indicating if we've found a panel card in the node
  var bFoundPanelCard = false;

  //if the content of the node is rendered, find the AfPanelCard
  //and change its displayed sdi

  //BUG FIX #7509913: get the first panel card from the node, because we
  //want to sync panel cards with different ids across node templates
  //if possible
  var panelCard = this.getPanelCard(node);
  if (panelCard) {
    //toggle the flag because we found a panel card
    bFoundPanelCard = true;
    //find the specified sdi
    //only loop over the visible detail items in this panel card
    numSdis = panelCard.getNumChildren();
    var sdiTempText = null;
    var sdiTempIcon = null;

    var deferLoading = panelCard.isPanelCardDeliveryLazy();

    for (j = 0; j < numSdis; j++) {
      //get this visible detail item
      sdiTemp = panelCard.getChildAt(j);
      //if this is the specified detail item and the panel card
      //is not currently showing it, switch to it
      sdiTempText = sdiTemp.getText();
      sdiTempIcon = sdiTemp.getIcon();
      //BUG FIX #7509913: match the sdi based on id, or text, or icon
      //so that we can sync panel cards with different ids in different node
      //templates if they're showing similar information
      if (sdiTemp.getId() == sdiId ||
          (sdiTempText && sdiTempText == sdiText) ||
          (sdiTempIcon && sdiTempIcon == sdiIcon)) {

        if (panelCard.getDisplayedDetailItemIndex() != j) {
          //defer panelCard loading
          if (deferLoading && ! node.getVisible() &&
              ! this.isShowDetailItemLoaded(nodeData.getId(), j)) {
            node.setStateProp(key, j);
          }
          else {
            panelCard.displayDetailItem(j);
          }
        }

        //we found the detail item, so break out of this loop
        break;
      }
    }
  }

  //if we didn't find a panel card in the node display objects,
  //find the AfPanelCardTemplate
  //in the node template and set the state property on the node to
  //indicate which sdi should be displayed when the
  //node content is rendered
  if (! bFoundPanelCard) {
    //if the node content is not rendered, search for a panel card
    //in the current node template

    //BUG FIX #7493071: if we still don't have a panel card, try
    //searching in the 100% zoom facet, if that's not the one
    //currently being displayed
    if (! panelCard) {
      panelCard = this._getFirstPanelCard(node);
    }

    if (panelCard) {
      //only loop over the visible detail items in this panel card
      numSdis = panelCard.getDetailItemCount();
      for (j = 0; j < numSdis; j++)
      {
        //get this visible detail item
        sdiTemp = panelCard.getVisibleChild(j);
        //if this is the specified detail item and the panel card
        //is not currently showing it, switch to it
        sdiTempText = sdiTemp.getText();
        sdiTempIcon = sdiTemp.getIcon();
        //BUG FIX #7509913: match the sdi based on id, or text, or icon
        //so that we can sync panel cards with different ids in different node
        //templates if they're showing similar information
        if (sdiTemp.getId() == sdiId ||
            (sdiTempText && sdiTempText == sdiText) ||
            (sdiTempIcon && sdiTempIcon == sdiIcon))
        {
          node.setStateProp(key, j);
          //we found the detail item, so break out of this loop
          break;
        }
      }
    }
  }
};

/*
dvt.AdfDiagram.prototype._syncPanelCardToParent = function(parentNode, pIndex) {
  var panelCard = this.getPanelCard(parentNode);
  if (panelCard) {
    var child = panelCard.getVisibleChild(panelCard.getDisplayedDetailItemIndex());

    if (child instanceof dvt.AfShowDetailItem) {
      var sdi = child;
      var sdiId = sdi.getId();
      var sdiText = sdi.getText();
      var sdiIcon = sdi.getIcon();

      this._syncChildren(parentNode, sdiId, sdiText, sdiIcon, pIndex);
    }
  }
}


dvt.AdfDiagram.prototype._syncChildren = function(node, sdiId, sdiText, sdiIcon, pIndex) {
  if (node) {
    this.syncOnePanelCard(node, sdiId, sdiId, sdiText, sdiIcon);

    var firstChildIndex = node.getFirstVisibleChildIndex();
    var lastChildIndex = node.getLastVisibleChildIndex();
    if (firstChildIndex > -1 && lastChildIndex > -1) {
      var nodeId;

      for (var j = firstChildIndex; j <= lastChildIndex; j++) {
        var childNode = node.getVisibleChildNode(j);
        this._syncChildren(childNode, sdiId, sdiId, sdiText, sdiIcon);

        //defer panelCard loading
        if (pIndex != -1) {
          nodeId = childNode.getData().getId();

          //remove the old one if exists
          this._cardIndexes = this.removeFromPanelCardList(nodeId);
          this._cardIndexes.push(new Array(nodeId, pIndex));
        }
      }
    }
  }
}
*/

/*-------------------------------------------------------------------------*/
/* panelCard skinning                                                      */

dvt.AdfDiagram.prototype.setCardStyle = function(cardCSSStyle) {
  this._cardCSSStyle = cardCSSStyle;
};

dvt.AdfDiagram.prototype.getCardStyle = function() {
  return this._cardCSSStyle;
};

dvt.AdfDiagram.prototype.setCardHeaderStyle = function(cardCSSStyle) {
  this._cardHeaderStyle = cardCSSStyle;
};
dvt.AdfDiagram.prototype.getCardHeaderStyle = function() {
  return this._cardHeaderStyle;
};

dvt.AdfDiagram.prototype.setCardNavButtonStyle = function(buttonStyle) {
  this._cardNavButtonStyle = buttonStyle;
};
dvt.AdfDiagram.prototype.getCardNavButtonStyle = function() {
  return this._cardNavButtonStyle;
};

dvt.AdfDiagram.prototype.setCardNavButtonHoverStyle = function(buttonHoverStyle) {
  this._cardNavButtonHoverStyle = buttonHoverStyle;
};
dvt.AdfDiagram.prototype.getCardNavButtonHoverStyle = function() {
  return this._cardNavButtonHoverStyle;
};

dvt.AdfDiagram.prototype.setCardNavButtonActiveStyle = function(buttonActiveStyle) {
  this._cardNavButtonActiveStyle = buttonActiveStyle;
};
dvt.AdfDiagram.prototype.getCardNavButtonActiveStyle = function() {
  return this._cardNavButtonActiveStyle;
};

dvt.AdfDiagram.prototype.setCardNavButtonDisabledStyle = function(buttonDisabledStyle) {
  this._cardNavButtonDisabledStyle = buttonDisabledStyle;
};
dvt.AdfDiagram.prototype.getCardNavButtonDisabledStyle = function() {
  return this._cardNavButtonDisabledStyle;
};

dvt.AdfDiagram.prototype.setCardTrainButtonVisible = function(visible) {
  this._cardTrainButtonVisible = visible;
};
dvt.AdfDiagram.prototype.isCardTrainButtonVisible = function() {
  return this._cardTrainButtonVisible;
};
dvt.AdfDiagram.prototype.setCardTrainButtonStyle = function(buttonStyle) {
  this._cardTrainButtonStyle = buttonStyle;
};
dvt.AdfDiagram.prototype.getCardTrainButtonStyle = function() {
  return this._cardTrainButtonStyle;
};
dvt.AdfDiagram.prototype.setCardTrainButtonHoverStyle = function(buttonHoverStyle) {
  this._cardTrainButtonHoverStyle = buttonHoverStyle;
};
dvt.AdfDiagram.prototype.getCardTrainButtonHoverStyle = function() {
  return this._cardTrainButtonHoverStyle;
};
dvt.AdfDiagram.prototype.setCardTrainButtonActiveStyle = function(buttonActiveStyle) {
  this._cardTrainButtonActiveStyle = buttonActiveStyle;
};
dvt.AdfDiagram.prototype.getCardTrainButtonActiveStyle = function() {
  return this._cardTrainButtonActiveStyle;
};
dvt.AdfDiagram.prototype.setCardNextButtonIcon = function(icon) {
  this._cardNextButtonIcon = icon;
};
dvt.AdfDiagram.prototype.getCardNextButtonIcon = function() {
  return this._cardNextButtonIcon;
};

dvt.AdfDiagram.prototype.setCardPrevButtonIcon = function(icon) {
  this._cardPrevButtonIcon = icon;
};
dvt.AdfDiagram.prototype.getCardPrevButtonIcon = function() {
  return this._cardPrevButtonIcon;
};

dvt.AdfDiagram.prototype._updatePanZoomState = function(reset) {
  var panZoomInfo;
  if (reset) {
    panZoomInfo = 'null';
  }
  else {
    var pzc = this.getPanZoomCanvas();
    panZoomInfo = pzc.getPanX() + dvt.AdfDiagram.ROWKEY_DELIMITER + pzc.getPanY() + dvt.AdfDiagram.ROWKEY_DELIMITER + pzc.getZoom();
  }

  var event = dvt.EventFactory.newAdfPropertyChangeEvent(dvt.PanZoomComponent.PAN_ZOOM_KEY, panZoomInfo);
  this.dispatchEvent(event);
};

dvt.AdfDiagram.prototype._updatePreviewStackPanZoomState = function(reset) {
  var panZoomInfo;
  if (reset) {
    panZoomInfo = 'null';
  }
  else {
    var pzc = this.getPanZoomCanvas();
    panZoomInfo = pzc.getPanX() + dvt.AdfDiagram.ROWKEY_DELIMITER + pzc.getPanY() + dvt.AdfDiagram.ROWKEY_DELIMITER + pzc.getZoom();
  }
  var event = dvt.EventFactory.newAdfPropertyChangeEvent(dvt.AdfDiagram.PREVIEW_PAN_ZOOM_KEY, panZoomInfo);
  this.dispatchEvent(event);
};

dvt.AdfDiagram.prototype.setStackPreviewColor = function(color) {
  this._stackPreviewColor = color;
};

dvt.AdfDiagram.prototype.getStackPreviewColor = function() {
  return this._stackPreviewColor;
};

dvt.AdfDiagram.prototype.setStackBadgeStyles = function(styles) {
  this._stackBadgeStyles = styles;
};

dvt.AdfDiagram.prototype.getStackBadgeStyles = function() {
  return this._stackBadgeStyles;
};

dvt.AdfDiagram.prototype.setStackUnderlayStyles = function(styles) {
  this._stackStackUnderlayStyles = styles;
};

dvt.AdfDiagram.prototype.getStackUnderlayStyles = function() {
  return this._stackStackUnderlayStyles;
};

/*
 * _diagramButtonStyles is and array of 3 DvtCSSStyles : ena, ovr, dwn
 */
dvt.AdfDiagram.prototype.setDiagramButtonStyles = function(styles) {
  this._diagramButtonStyles = styles;
};

dvt.AdfDiagram.prototype.getDiagramButtonStyles = function() {
  return this._diagramButtonStyles;
};


/**
 * Helper method that returns border radius for the Daigram button
 * @param {Array} styles An array that contains button styles for enabled, hover and down states. Each state is a dvt.CSSStyle object
 * @return {number} border radius for the diagram button
 */
dvt.AdfDiagram.getButtonBorderRadius = function(styles) {
  var rx = 0;
  if (styles && styles[dvt.AdfDiagram.BUTTON_STATE_ENA] && styles[dvt.AdfDiagram.BUTTON_STATE_ENA] instanceof dvt.CSSStyle) {
    rx = styles[dvt.AdfDiagram.BUTTON_STATE_ENA].getBorderRadius();
  }
  return rx;
};


/**
 * Helper method that returns fill object for the Diagram button
 * @param {Array} styles An array that contains button styles for enabled, hover and down states. Each state is a dvt.CSSStyle object.
 * @param {number} state A button state that could be one of the following values -
 *                       dvt.AdfDiagram.BUTTON_STATE_ENA, dvt.AdfDiagram.BUTTON_STATE_OVR or dvt.AdfDiagram.BUTTON_STATE_DWN
 * @return {dvt.Fill} button fill
 */
dvt.AdfDiagram.getButtonFill = function(styles, state) {
  var fill = null;
  if (styles && styles[state]) {
    var style = styles[state];
    if (style) {
      var bgColor = style.getStyle(dvt.CSSStyle.BACKGROUND_COLOR);
      var gradient = style.getBackgroundImage();
      fill = dvt.AdfDiagram.CreateFill(bgColor, gradient);
    }
  }
  return fill;
};


/**
 * Helper method that returns stroke object for the Diagram button
 * @param {Array} styles An array that contains button styles for enabled, hover and down states. Each state is a dvt.CSSStyle object.
 * @param {number} state A button state that could be one of the following values -
 *                       dvt.AdfDiagram.BUTTON_STATE_ENA, dvt.AdfDiagram.BUTTON_STATE_OVR or dvt.AdfDiagram.BUTTON_STATE_DWN
 * @return {dvt.Stroke} button stroke
 */
dvt.AdfDiagram.getButtonStroke = function(styles, state) {
  var stroke = null;
  var strokeColor;
  if (styles && styles[state]) {
    var style = styles[state];
    strokeColor = style.getStyle(dvt.CSSStyle.BORDER_COLOR);
    if (!strokeColor)
      strokeColor = style.getStyle(dvt.CSSStyle.BORDER_TOP_COLOR);
    if (strokeColor)
      stroke = new dvt.SolidStroke(strokeColor, 1, 1);
  }

  return stroke;
};


/**
 * @protected
 * Helper method that creates a fill using specified color or gradient object.
 * @param {String} color A css color specification for the fill color.
 * @param {dvt.CSSGradient} gradObj A css gradient object
 * @return {dvt.Fill} fill object
 */
dvt.AdfDiagram.CreateFill = function(color, gradObj) {

  var fill = null;
  if (gradObj && (gradObj instanceof dvt.CSSGradient)) {
    var arColors = gradObj.getColors();
    var arAlphas = gradObj.getAlphas();
    var arStops = gradObj.getRatios();
    var angle = gradObj.getAngle();
    fill = new dvt.LinearGradientFill(angle, arColors, arAlphas, arStops);
  }
  else if (color)
    fill = new dvt.SolidFill(color, 1);
  return fill;
};

dvt.AdfDiagram.prototype.CreateDropShadowFilter = function(shadowStyle) {
  //Bug 15880587: SVG filter effects in Firefox look blurry, so don't create a shadow
  if (dvt.Agent.isPlatformGecko()) {
    return null;
  }

  return dvt.StyleUtils.getShadow(shadowStyle);
};

/**
 * @override
 */
dvt.AdfDiagram.prototype.GetControlPanel = function() {
  var cpBehavior = this.GetControlPanelBehavior();
  if (cpBehavior != dvt.PanZoomComponent.CONTROL_PANEL_BEHAVIOR_HIDDEN) {
    var cpState = (cpBehavior == dvt.PanZoomComponent.CONTROL_PANEL_BEHAVIOR_INIT_COLLAPSED ?
        dvt.ControlPanel.STATE_COLLAPSED : dvt.ControlPanel.STATE_EXPANDED);
    return new dvt.AdfDiagramControlPanel(this.getCtx(), this, cpState);
  } else {
    return null;
  }
};

dvt.AdfDiagram.prototype.setSyncCardOff = function(bSyncCardOff) {
  this._bSyncCardOff = bSyncCardOff;
};

dvt.AdfDiagram.prototype.isSyncCardOff = function() {
  return this._bSyncCardOff;
};

/**
 * Get Show Popup behaviors
 * @return {Array}  Array of dvt.AfShowPopupBehavior
 */
dvt.AdfDiagram.prototype.getShowPopupBehaviors = function() {
  return this._showPopupBehaviors;
};

/**
 * Add Show Popup behavior
 * @param {dvt.AfShowPopupBehavior} spub  Show Popup Behavior
 */
dvt.AdfDiagram.prototype.addShowPopupBehavior = function(spub) {
  if (! this._showPopupBehaviors) {
    this._showPopupBehaviors = [];
  }
  this._showPopupBehaviors.push(spub);
};

//Bug 14851707: use the list of node ids that will be laid out
//to narrow down the set of nodes to check when determining whether
//a node isolate is valid
/**
 * @protected
 * Get the list of node ids that participate in layout.
 */
dvt.AdfDiagram.prototype.GetLayoutNodeIdsList = function() {
  //NOTE: the return value must be the final result of the last phase of the
  //dvt.AdfDiagramTransitionUtils processing sequence, so must be kept in sync
  //with that file
  return this._nodesPostContainerDisclosure;
};

/**
 * @protected
 * Determine whether a node isolate is valid.  It is valid when the
 * set of visible diagram nodes and links will change after the isolate.
 * @param {string} isolateId  isolated node id
 * @return {boolean} flag indicates if the node isolate is valid or not
 */
dvt.AdfDiagram.prototype.IsNodeIsolateValid = function(isolateId) {
  //Bug 14851707:
  //not valid when all other nodes are either descendants of the given node
  //or hidden ancestors of the given node,or when all other nodes are in the
  //same stack
  var nodeIds = this.GetLayoutNodeIdsList();
  if (!nodeIds || (nodeIds.length == 1 && isolateId == nodeIds[0])) {
    return false;
  }

  //isolate on a stack is valid if we can find a node that is not in the
  //stack and is not a hidden ancestor of the isolated node
  var isolateNode = this.getNodeById(isolateId);
  var bStack = isolateNode.isStack();
  if (bStack) {
    var isolateStackName = this._nodeStackMap[isolateId];
    //var stackNodes = this._stackNodeMap[isolateStackName];
    //var attributeGroups = this.getAttributeGroups();

    for (var i = 0; i < nodeIds.length; i++) {
      var nodeId = nodeIds[i];
      //skip the node being isolated
      if (nodeId != isolateId) {
        var stackName = this._nodeStackMap[nodeId];
        //if the node is in the same stack, just continue on;
        //if the node is not in the same stack, and it is not a hidden
        //ancestor of the isolated node, then it's a valid isolate because
        //the node will disappear
        if (stackName != isolateStackName) {
          var node = this.getNodeById(nodeId);
          var bHiddenAncestor = this.IsAncestorNode(nodeId, isolateId) && node.isHiddenAncestor();
          if (!bHiddenAncestor) {
            return true;
          }
        }
      }
    }
  }
  //else not a stacked node
  else {
    //isolate is valid if we find a node that is not a descendant of the
    //isolated node, and is not a hidden ancestor of the isolated node
    for (var i = 0; i < nodeIds.length; i++) {
      var nodeId = nodeIds[i];
      //skip the node being isolated
      if (nodeId != isolateId) {
        var node = this.getNodeById(nodeId);
        var bDescendant = this.IsAncestorNode(isolateId, nodeId);
        if (!bDescendant) {
          var bHiddenAncestor = this.IsAncestorNode(nodeId, isolateId) && node.isHiddenAncestor();
          if (!bHiddenAncestor) {
            return true;
          }
        }
      }
    }
  }

  return false;
};

/**
 * @protected
 * Determine whether a node is an ancestor of another node.
 * @param {string} ancestorId  ancestor node id
 * @param {string} descendantId  descendant node id
 * @return {boolean} flag indicates whether a node is an ancestor of another node
 */
dvt.AdfDiagram.prototype.IsAncestorNode = function(ancestorId, descendantId) {
  //Bug 14851707:
  var descendantNode = this.getNodeById(descendantId);
  var groupId = descendantNode.getData().getGroupId();
  while (groupId) {
    if (groupId == ancestorId) {
      return true;
    }
    var groupNode = this.getNodeById(groupId);
    groupId = groupNode.getData().getGroupId();
  }
  return false;
};


/**
 * @return {dvt.AdfDiagramAutomationBase} the automation object
 */
dvt.AdfDiagram.prototype.getAutomation = function() {
  if (!this.Automation)
    this.Automation = new DvtAdfDiagramAutomation(this);
  return this.Automation;
};

dvt.AdfDiagram.prototype.GetXmlComponentName = function() {
  return 'dg';
};


/**
 * Process the JSON object
 * @param {object} jsonObj  JSON object to process
 * @return {object}  processed JSON object
 */
dvt.AdfDiagram.prototype.ProcessJSON = function(jsonObj) {
  //a json string?
  //Should not convert the JSON to XML
  /*if (! DvtAdfDiagramJsonUtils.isXmlStartChar(xmlString)) {
    // Update if new data has been provided. Clone to avoid modifying the provided object.
    this._data = dvt.JsonUtils.clone(xmlString);
    if (! this._data)
      this._data = {};

    // Convert the data into XML
    xmlString = (new DvtAdfDiagramJsonUtils()).toXml(this.GetXmlComponentName(), this._data);
  }*/

  return jsonObj;
};

dvt.AdfDiagram.prototype.getSortedVisibleNodesByGroupDepth = function() {
  if (!this._visibleNodesByGroupDepth)
    this._visibleNodesByGroupDepth = this.SortNodesByGroupDepth(false);

  return this._visibleNodesByGroupDepth;
};

dvt.AdfDiagram.prototype.resetSortedNodesArray = function() {
  this._visibleNodesByGroupDepth = null;
};


/**
 * Resets diagram nodes and links - saves old nodes, links and related objects, repopulates new nodes and links structures with data if necessary
 * @param {boolean} bRepopulateNodesAndLinks Whether to copy old nodes and links to the new nodes and links structures
 * @protected
 */
dvt.AdfDiagram.prototype.ResetNodesAndLinks = function(bRepopulateNodesAndLinks) {
  //need to create the new nodes and links panes before creating
  //new nodes and links and adding them as children
  var linksPaneIndex = this._linksPane.getParent().getChildIndex(this._linksPane);
  var nodesPaneIndex = this._nodesPane.getParent().getChildIndex(this._nodesPane);
  var topPaneIndex = this._topPane.getParent().getChildIndex(this._topPane);
  this._oldLinksPane = this._linksPane;
  this._oldNodesPane = this._nodesPane;
  this._oldTopPane = this._topPane;
  this._linksPane = new dvt.Container(this.getCtx());
  this._nodesPane = new dvt.Container(this.getCtx());
  this._topPane = new dvt.Container(this.getCtx());
  this._oldLinksPane.getParent().addChildAt(this._linksPane, linksPaneIndex);
  this._oldLinksPane.getParent().removeChild(this._oldLinksPane);
  this._oldNodesPane.getParent().addChildAt(this._nodesPane, nodesPaneIndex);
  this._oldNodesPane.getParent().removeChild(this._oldNodesPane);
  this._oldTopPane.getParent().addChildAt(this._topPane, topPaneIndex);
  this._oldTopPane.getParent().removeChild(this._oldTopPane);

  this._oldLinksPane.setAlpha(0);
  this._oldLinksPane.setMouseEnabled(false);
  this._oldNodesPane.setAlpha(0);
  this._oldNodesPane.setMouseEnabled(false);
  this._oldTopPane.setAlpha(0);
  this._oldTopPane.setMouseEnabled(false);
  this.addChild(this._oldLinksPane);
  this.addChild(this._oldNodesPane);
  this.addChild(this._oldTopPane);

  //create new nodes
  this._oldNodes = this._nodes;
  this._oldNodesByRowIndex = this._nodesByRowIndex;
  this._oldArNodeIds = this._arNodeIds;
  this._nodes = {};
  this._nodesByRowIndex = {};
  this._arNodeIds = [];
  this._rootNodes = [];

  if (bRepopulateNodesAndLinks) {
    for (var i = 0; i < this._oldArNodeIds.length; i++) {
      var nodeId = this._oldArNodeIds[i];
      var node = this._oldNodes[nodeId];
      this.addNodeData(node.getData());
      this.getNodeById(nodeId).CopyState(node);
    }
  }

  //create new links
  this._oldLinks = this._links;
  this._oldLinksByRowIndex = this._linksByRowIndex;
  this._oldArLinkIds = this._arLinkIds;
  this._links = {};
  this._linksByRowIndex = {};
  this._arLinkIds = [];
  this._arCrossContainerLinkIds = null;

  if (bRepopulateNodesAndLinks) {
    for (var i = 0; i < this._oldArLinkIds.length; i++) {
      var linkId = this._oldArLinkIds[i];
      var link = this._oldLinks[linkId];
      this.addLinkData(link.getData());
      this.getLinkById(linkId).CopyState(link);
    }
  }

  //drill breadcrumbs will be recreated by new view if necessary
  if (this._drillBreadcrumbs) {
    this.RemoveDrillBreadcrumbs();
    this.AdjustViewSpace(0);
  }

  //recreate overview content in the new view
  this._overviewContent = null;
  this._overviewNodes = null;
};

dvt.AdfDiagram.prototype.CreateDataAnimationState = function() {
  var obj = {panZoomMatrix: this.getPanZoomCanvas().getContentPane().getMatrix(),
    panZoomCanvasTy: this.getPanZoomCanvas().getTranslateY(),
    panZoomCanvasSize: this.getPanZoomCanvas().getSize(),
    panelDrawerTy: this._panelDrawer ? this._panelDrawer.getTranslateY() : null,
    restoreButtonTy: this._restoreButton ? this._restoreButton.getTranslateY() : null,
    drillBreadcrumbs: this._drillBreadcrumbs,
    stackNodeMap: this._stackNodeMap,
    nodeStackMap: this._nodeStackMap,
    arNodeIds: this._arNodeIds,
    arLinkIds: this._arLinkIds};
  if (this._overviewWindow) {
    obj.overviewViewportRect = this._overviewWindow.GetViewportRectangle();
    obj.overviewContent = this._overviewContent;
    obj.overviewNodes = this._overviewNodes;
  }
  return obj;
};

dvt.AdfDiagram.prototype.CreateCustomAnimPlayable = function() {
  return new dvt.CustomAnimation(this.getCtx(), null, this.getAnimationDuration());
};


/**
 * Set Button Style
 * @param {dvt.CSSStyle} containerStyle  Button container style
 */
dvt.AdfDiagram.prototype.setButtonStyle = function(containerStyle) {
  this._buttonStyle = containerStyle;
};


/**
 * Get Button Style
 * @return {dvt.CSSStyle}  Button container style
 */
dvt.AdfDiagram.prototype.getButtonStyle = function() {
  return this._buttonStyle;
};


/**
 * Set Label style
 * @param {dvt.CSSStyle} labelStyle  Button label style
 */
dvt.AdfDiagram.prototype.setLabelStyle = function(labelStyle) {
  this._labelStyle = labelStyle;
};


/**
 * Get Label style
 * @return {dvt.CSSStyle}  Button label style
 */
dvt.AdfDiagram.prototype.getLabelStyle = function() {
  return this._labelStyle;
};

dvt.AdfDiagram.prototype.SetPanZoomCanvasHeight = function(hh) {
  this.getPanZoomCanvas().setSize(this.getPanZoomCanvas().getSize().w, hh);
};

dvt.AdfDiagram.prototype.GetPanZoomCanvasHeight = function(hh) {
  return this.getPanZoomCanvas().getSize().h;
};

dvt.AdfDiagram.prototype.AnimateUpdate = function(dataAnimHandler, oldDiagramState) {
  var playable = null;

  //determine which stacks were created and which were exploded in order
  //to save the original stack positions so that the dis/appearing
  //nodes can transition to/from the stack in dvt.AdfDiagramNode.AnimateUpdate
  var createdStacks = {};
  var explodedStacks = {};
  if (!oldDiagramState.stackNodeMap && this._stackNodeMap) {
    for (var stackName in this._stackNodeMap) {
      createdStacks[stackName] = true;
    }
  }
  else if (oldDiagramState.stackNodeMap && !this._stackNodeMap) {
    for (var stackName in oldDiagramState.stackNodeMap) {
      explodedStacks[stackName] = true;
    }
  }
  else if (oldDiagramState.stackNodeMap && this._stackNodeMap) {
    for (var stackName in oldDiagramState.stackNodeMap) {
      if (!this._stackNodeMap[stackName]) {
        explodedStacks[stackName] = true;
      }
    }
    for (var stackName in this._stackNodeMap) {
      if (!oldDiagramState.stackNodeMap[stackName]) {
        createdStacks[stackName] = true;
      }
    }
  }
  for (var stackName in explodedStacks) {
    //if the stack is exploded, then get the list of nodes in the
    //stack from the old diagram
    var arNodes = oldDiagramState.stackNodeMap[stackName];
    var nodeId = arNodes[0];
    //we want the nodes to move out from the stack during the insert
    //phase, so use the position of the node in the new diagram as
    //the starting point
    var node = this._nodes[nodeId];
    var stackPos = new dvt.Point(node.getTranslateX(), node.getTranslateY());
    for (var i = 1; i < arNodes.length; i++) {
      this._nodes[arNodes[i]]._dataAnimStackPos = stackPos;
    }
  }
  for (var stackName in createdStacks) {
    //if the stack is created, then get the list of nodes in the
    //stack from the new diagram
    var arNodes = this._stackNodeMap[stackName];
    var nodeId = arNodes[0];
    //we want the nodes to move into the stack during the delete
    //phase, so use the position of the node in the old diagram as
    //the destination point
    var node = this._oldNodes[nodeId];
    var stackPos = new dvt.Point(node.getTranslateX(), node.getTranslateY());
    for (var i = 1; i < arNodes.length; i++) {
      this._nodes[arNodes[i]]._dataAnimStackPos = stackPos;
    }
  }

  var oldMat = oldDiagramState.panZoomMatrix;
  var newMat = this.getPanZoomCanvas().getContentPane().getMatrix();
  if (!oldMat.equals(newMat)) {
    if (!playable) {
      playable = this.CreateCustomAnimPlayable();
    }
    this.getPanZoomCanvas().getContentPane().setMatrix(oldMat);
    playable.getAnimator().addProp(dvt.Animator.TYPE_MATRIX, this.getPanZoomCanvas().getContentPane(), this.getPanZoomCanvas().getContentPane().getMatrix, this.getPanZoomCanvas().getContentPane().setMatrix, newMat);
  }

  //may need to move the panZoomCanvas for drill breadcrumbs
  var oldPzcTy = oldDiagramState.panZoomCanvasTy;
  var newPzcTy = this.getPanZoomCanvas().getTranslateY();
  if (oldPzcTy != newPzcTy) {
    if (!playable) {
      playable = this.CreateCustomAnimPlayable();
    }
    this.getPanZoomCanvas().setTranslateY(oldPzcTy);
    playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, this.getPanZoomCanvas(), this.getPanZoomCanvas().getTranslateY, this.getPanZoomCanvas().setTranslateY, newPzcTy);
  }

  //may need to adjust the panZoomCanvas size for drill breadcrumbs
  var oldPzcSize = oldDiagramState.panZoomCanvasSize;
  var newPzcSize = this.getPanZoomCanvas().getSize();
  if (oldPzcSize.h != newPzcSize.h) {
    if (!playable) {
      playable = this.CreateCustomAnimPlayable();
    }
    this.SetPanZoomCanvasHeight(oldPzcSize.h);
    playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, this, this.GetPanZoomCanvasHeight, this.SetPanZoomCanvasHeight, newPzcSize.h);
  }

  //may need to move the panelDrawer for drill breadcrumbs
  var oldPanelDrawerTy = oldDiagramState.panelDrawerTy;
  if (this._panelDrawer && oldPanelDrawerTy != null) {
    var newPanelDrawerTy = this._panelDrawer.getTranslateY();
    if (oldPanelDrawerTy != newPanelDrawerTy) {
      if (!playable) {
        playable = this.CreateCustomAnimPlayable();
      }
      this._panelDrawer.setTranslateY(oldPanelDrawerTy);
      playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, this._panelDrawer, this._panelDrawer.getTranslateY, this._panelDrawer.setTranslateY, newPanelDrawerTy);
    }
  }

  //may need to move the restoreButton for drill breadcrumbs
  var oldRestoreButtonTy = oldDiagramState.restoreButtonTy;
  if (this._restoreButton && oldRestoreButtonTy != null) {
    var newRestoreButtonTy = this._restoreButton.getTranslateY();
    if (oldRestoreButtonTy != newRestoreButtonTy) {
      if (!playable) {
        playable = this.CreateCustomAnimPlayable();
      }
      this._restoreButton.setTranslateY(oldRestoreButtonTy);
      playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, this._restoreButton, this._restoreButton.getTranslateY, this._restoreButton.setTranslateY, newRestoreButtonTy);
    }
  }

  var oldDrillBreadcrumbs = oldDiagramState.drillBreadcrumbs;
  var newDrillBreadcrumbs = this._drillBreadcrumbs;
  //fade in new breadcrumbs and fade out old ones
  if (oldDrillBreadcrumbs && newDrillBreadcrumbs) {
    if (!playable) {
      playable = this.CreateCustomAnimPlayable();
    }
    newDrillBreadcrumbs.setAlpha(0);
    this.addChild(oldDrillBreadcrumbs);
    playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, oldDrillBreadcrumbs, oldDrillBreadcrumbs.getAlpha, oldDrillBreadcrumbs.setAlpha, 0);
    playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, newDrillBreadcrumbs, newDrillBreadcrumbs.getAlpha, newDrillBreadcrumbs.setAlpha, 1);
    dvt.Playable.appendOnEnd(playable, function() {
      oldDrillBreadcrumbs.getParent().removeChild(oldDrillBreadcrumbs);
      oldDrillBreadcrumbs.destroy();
    });
  }
  //fade in new breadcrumbs during insert phase
  else if (newDrillBreadcrumbs) {
    var insertPlayable = this.CreateCustomAnimPlayable();
    this._drillBreadcrumbs.setAlpha(0);
    insertPlayable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, this._drillBreadcrumbs, this._drillBreadcrumbs.getAlpha, this._drillBreadcrumbs.setAlpha, 1);
    dataAnimHandler.add(insertPlayable, DvtAdfDiagramDataAnimationHandler.PHASE_INSERT);
  }
  //fade out old breadcrumbs during delete phase
  else if (oldDrillBreadcrumbs) {
    var deletePlayable = this.CreateCustomAnimPlayable();
    this.addChild(oldDrillBreadcrumbs);
    deletePlayable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, oldDrillBreadcrumbs, oldDrillBreadcrumbs.getAlpha, oldDrillBreadcrumbs.setAlpha, 0);
    dvt.Playable.appendOnEnd(deletePlayable, function() {
      oldDrillBreadcrumbs.getParent().removeChild(oldDrillBreadcrumbs);
      oldDrillBreadcrumbs.destroy();
    });
    dataAnimHandler.add(deletePlayable, DvtAdfDiagramDataAnimationHandler.PHASE_DELETE);
  }

  if (oldDiagramState.overviewViewportRect && oldDiagramState.overviewContent && oldDiagramState.overviewNodes &&
      this._overviewWindow && this._overviewContent && this._overviewNodes) {
    DvtAdfDiagramOverviewUtils.AnimateUpdate(this, dataAnimHandler, oldDiagramState.overviewViewportRect, this._overviewWindow,
        oldDiagramState.overviewContent, this._overviewContent,
        oldDiagramState.overviewNodes, this._overviewNodes,
        oldDiagramState.arNodeIds, this._arNodeIds);
  }

  if (playable) {
    dataAnimHandler.add(playable, DvtAdfDiagramDataAnimationHandler.PHASE_UPDATE);
  }
};


/**
 * Returns the overview window panel, either a dvt.CollapsiblePanel or a dvt.PanelDrawer.
 */
dvt.AdfDiagram.prototype.__getPanelDrawer = function()
{
  return this._panelDrawer;
};

/**
 * Gets an array of navigable links for the specified node
 * @param {string} nodeId node id
 * @return {array} array of navigable links for the specified node
 */
dvt.AdfDiagram.prototype.getNavigableLinksForNodeId = function(nodeId) {
  var links = [];
  for (var linkId in this._links) {
    var link = this.getLinkById(linkId);
    var startId = this.getLinkStartId(link);
    var endId = this.getLinkEndId(link);

    if ((startId == nodeId || endId == nodeId) && link.getVisible())
      links.push(link);
  }
  return links;
};

dvt.AdfDiagram.prototype.getLinkStartId = function(link) {
  var startId = link.isPromoted() ? this._containerLinkPromotionMap[link.getData().getId()][0] :
                                    link.getData().getStartId();
  return startId;
};

dvt.AdfDiagram.prototype.getLinkEndId = function(link) {
  var endId = link.isPromoted() ? this._containerLinkPromotionMap[link.getData().getId()][1] :
      link.getData().getEndId();
  return endId;
};

dvt.AdfDiagram.prototype.getShapesForViewSwitcher = function(bOld) {
  var shapes = {};

  //preliminary simple implementation just uses node content and assumes no open containers
  for (var nodeId in this._nodes) {
    var node = this._nodes[nodeId];
    if (node.getVisible()) {
      shapes[nodeId] = node._afRoot;
    }
  }

  return shapes;
};


dvt.AdfDiagram.prototype.isDragged = function(event) {
  return this._bDragged;
};


/**
 * Returns an object that contains style options that used to initialize breadcrumbs for the component
 * @return {object} The object containing style options for breadcrumbs.
 */
dvt.AdfDiagram.prototype.getBreadcrumbsOptions = function() {

  var enabledStyle = this.getBreadcrumbsStyles(dvt.AdfDiagram.BUTTON_STATE_ENA);
  var overStyle = this.getBreadcrumbsStyles(dvt.AdfDiagram.BUTTON_STATE_OVR);
  var downStyle = this.getBreadcrumbsStyles(dvt.AdfDiagram.BUTTON_STATE_DWN);
  var selectedStyle = this.getBreadcrumbsStyles(dvt.AdfDiagram.BUTTON_STATE_DIS);

  var options = {labelStyle: enabledStyle,
    labelStyleOver: overStyle,
    labelStyleDown: downStyle,
    disabledLabelStyle: selectedStyle};
  return options;
};


/**
 * The method is called by the peer after a PPR. The method prepares/reinitializes the component for the new data
 * and it stores the old data that will be used to animate a transition from the old state to the new state.
 * The animation will happen later when component is rendered. See dvt.AdfDiagramTransitionUtils.__animateTransition() call in dvt.AdfDiagram.prototype.RenderComponentInternal()
 */
dvt.AdfDiagram.prototype.prepareForDataChange = function() {

  if (this._bRendered) { // make the changes if the component was previously rendered

    this.StopAnimation(true);
    //reset the states and store original data for the animation that will happen later
    dvt.AdfDiagramTransitionUtils.__prepForDataAnim(this, false);
    this._resetTransitionState();

    // Reset flags to force diagram rerendering
    this._bRendered = false;
    this._visibleNodesByGroupDepth = null;

    //Set the flag to indicate that diagram has old data for transitioning from one state to another
    if ('auto' == this.getDataChangeAnim()) {
      this._bAnimTransition = true;
    }
    else {
      this._oldLinksPane.getParent().removeChild(this._oldLinksPane);
      this._oldNodesPane.getParent().removeChild(this._oldNodesPane);
      this._oldTopPane.getParent().removeChild(this._oldTopPane);
      this.destroyOldContent();
    }

    //save viewport for the layout
    this._currentViewport = this.getPanZoomCanvas().getViewport();

    //Remove the listeners if they were attached to the component by previous operations and reinitialize the _bDragged state
    this.removeEvtListener(dvt.MouseEvent.MOUSEMOVE, this.HandleMouseMove, false, this);
    this.removeEvtListener(dvt.MouseEvent.MOUSEUP, this.HandleMouseUp, false, this);
    this._bDragged = false;
  }
  else if (this.GetAllNodes().length > 0) {
    // reinitialize members if the node array is not empty - the nodes could be added to the empty diagram by DnD
    this._nodes = {};
    this._nodesByRowIndex = {};
    this._arNodeIds = [];
    this._rootNodes = [];
    this._links = {};
    this._linksByRowIndex = {};
    this._arLinkIds = [];

    //Remove the listeners if they were attached to the component by previous operations and reinitialize the _bDragged state
    this.removeEvtListener(dvt.MouseEvent.MOUSEMOVE, this.HandleMouseMove, false, this);
    this.removeEvtListener(dvt.MouseEvent.MOUSEUP, this.HandleMouseUp, false, this);
    this._bDragged = false;
  }
};


/**
 * Sets palette styles for the component
 * @param {object} styles Palette styles
 */
dvt.AdfDiagram.prototype.setPaletteStyles = function(styles) {
  this._paletteStyles = styles;
};


/**
 * Gets palette styles for the component
 * @return {object} Palette styles
 */
dvt.AdfDiagram.prototype.getPaletteStyles = function() {
  return this._paletteStyles;
};


/**
 * Destroys the old nodes and links after a PPR
 */
dvt.AdfDiagram.prototype.destroyOldContent = function() {
  if (this._oldArNodeIds) {
    for (var i = this._oldArNodeIds.length - 1; i >= 0; i--) {
      var nodeId = this._oldArNodeIds[i];
      var node = this._oldNodes[nodeId];
      node.destroy();
    }
  }
  if (this._oldArLinkIds) {
    for (var i = this._oldArLinkIds.length - 1; i >= 0; i--) {
      var linkId = this._oldArLinkIds[i];
      var link = this._oldLinks[linkId];
      link.destroy();
    }
  }
  this._oldNodes = null;
  this._oldNodesByRowIndex = null;
  this._oldArNodeIds = null;

  this._oldLinks = null;
  this._oldLinksByRowIndex = null;
  this._oldArLinkIds = null;
};


/**
 * @private
 * Position a center node if the node is specified
 * @param {boolean} clearNode True to clear the centerNode
 * @param {dvt.Animator} animator the animator to animate the changes into
 */
dvt.AdfDiagram.prototype._processCenterNode = function(clearNode, animator) {
  if (this._centerNode) {
    var node = this.getNodeById(this._centerNode);
    if (node && node.getVisible()) {
      var pzc = this.getPanZoomCanvas();
      var contentBounds = node.getContentBounds();
      var centerPoint = new dvt.Point(contentBounds.x + .5 * contentBounds.w, contentBounds.y + .5 * contentBounds.h);
      var stageCenterPoint = this.LocalToStage(centerPoint, node, animator);
      var localCenterPoint = this.StageToLocal(stageCenterPoint, pzc.getContentPane(), animator);
      var zoom = pzc.getZoom(animator);
      pzc.panTo(this.Width / 2 - localCenterPoint.x * zoom, this.Height / 2 - localCenterPoint.y * zoom, animator);
    }
    if (clearNode)
      this._centerNode = null;
  }
};


/**
 * @private
 * Pan/zoom to the stored pan/zoom settings and clear them if needed
 * @param {boolean} clearSettings True to clear the settings
 * @param {dvt.Animator} animator the animator to animate the changes into
 */
dvt.AdfDiagram.prototype._processPanZoomSettings = function(clearSettings, animator) {
  //Bug 16695081: don't override a viewport returned from the layout engine
  if (this._panZoom && !this.IsLayoutViewport()) {
    this._setRerenderImmediate(true);
    this.getPanZoomCanvas().zoomTo(this._panZoom.zoom, 0, 0, animator);
    this.getPanZoomCanvas().panTo(this._panZoom.panX, this._panZoom.panY, animator);
    if (clearSettings)
      this._panZoom = null;
    this._setRerenderImmediate(false);
  }
};


/**
 * @private
 * Clears panning constraints - we might want to call this function before rerendering the component, e.g. when images are loaded
 */
dvt.AdfDiagram.prototype._clearPanningConstraints = function() {
  var pzc = this.getPanZoomCanvas();
  pzc.setMinPanX(null);
  pzc.setMinPanY(null);
  pzc.setMaxPanX(null);
  pzc.setMaxPanY(null);
};

/**
 * @private
 * Sets a flag for zoom event handler. When the flag is set to true the component skips the timer and rerenders immediately.
 * @param {boolean} rerenderImmediate True to skip the timer and rerender immediately
 */
dvt.AdfDiagram.prototype._setRerenderImmediate = function(rerenderImmediate) {
  this._rerenderImmediate = rerenderImmediate;
};

/**
 * @private
 * Gets the flag for zoom event handler.
 * @return {boolean} true to skip the timer and rerender immediately
 */
dvt.AdfDiagram.prototype._getRerenderImmediate = function() {
  return this._rerenderImmediate;
};

/**
 * Convert showDetailItem stamp id into associated index in card selector list.
 *
 * @param {string} id stampId of panel card
 * @return {number} index of panel card with given id
 */
dvt.AdfDiagram.prototype.convertPanelCardIdToIndex = function(id) {
  if (this._cardSelectorList) {
    for (var i = 0; i < this._cardSelectorList.length; i++) {
      if (this._cardSelectorList[i][2] == id) {
        return i;
      }
    }
  }
  return -1;
};

/**
 * Convert card selector list index into associated showDetailItem stamp id
 *
 * @param {number} index index of card to get id for
 * @return {string} stampId of panel card at given index
 */
dvt.AdfDiagram.prototype.convertIndexToPanelCardId = function(index) {
  if (this._cardSelectorList) {
    return this._cardSelectorList[index] ? this._cardSelectorList[index][2] : null;
  }
  return null;
};

/**
 * Set component summary
 * @param {string} summary component summary
 */
dvt.AdfDiagram.prototype.setSummary = function(summary) {
  this._summary = summary;
};


/**
 * Get component summary
 * @return {string} component summary
 */
dvt.AdfDiagram.prototype.getSummary = function() {
  return this._summary;
};


/**
 * Gets states for the component that are going to be used for WAI-ARIA label
 * @return {array} array of states for the component
 */
dvt.AdfDiagram.prototype.getAccessibilityStates = function() {
  var states = [];
  if (this._drilledNodePath) {
    states.push(dvt.Bundle.getTranslatedString(dvt.Bundle.ADF_DIAGRAM_PREFIX, 'STATE_DRILLED'));
  }
  if (this.IsStackPreview()) {
    states.push(dvt.Bundle.getTranslatedString(dvt.Bundle.ADF_DIAGRAM_PREFIX, 'STATE_PREVIEW_MODE'));
    states.push(dvt.Bundle.getTranslatedString(dvt.Bundle.UTIL_PREFIX, 'COLON_SEP_LIST',
        [dvt.Bundle.getTranslatedString(dvt.Bundle.ADF_DIAGRAM_PREFIX, 'SIZE'), this.getNodeById(this.GetStackPreviewNodeId()).getStackCount()]));
  }
  return states;
};

/**
 * @override
 */
dvt.AdfDiagram.prototype.GetComponentDescription = function() {
  var desc = dvt.Displayable.generateAriaLabel(this.getSummary(), this.getAccessibilityStates());
  if (desc)
    return dvt.Bundle.getTranslatedString(dvt.Bundle.UTIL_PREFIX, 'COLON_SEP_LIST', [dvt.Bundle.getTranslatedString(dvt.Bundle.UTIL_PREFIX, 'DIAGRAM'), desc]);
  else
    return dvt.Bundle.getTranslatedString(dvt.Bundle.UTIL_PREFIX, 'DIAGRAM');
};

/**
 * Set context keyboard handlers
 */
dvt.AdfDiagram.prototype.setContextKeyboardHandlers = function() {
  var handlers = [this];
  if (this._panelDrawer) {
    handlers.push(this._panelDrawer);
    if (this._panelDrawer.isDisclosed() && this._panelDrawer.getDisplayedPanelId() == dvt.PanelDrawer.PANEL_PALETTE) {
      handlers.push(this._palette);
    }
    if (this._panelDrawer.isDisclosed() && this._panelDrawer.getDisplayedPanelId() == dvt.PanelDrawer.PANEL_LEGEND) {
      handlers.push(this._legend);
    }
  }
  if (this._overviewWindowPanel && this._overviewWindowPanel != this._panelDrawer) {
    handlers.push(this._overviewWindowPanel);
  }
  this.getCtx().setKeyboardFocusArray(handlers);
};

/**
 * Store the automation state for the given id, state and its value
 *
 * @param {string} id     component id
 * @param {string} state  state key that needs to be stored
 * @param {object} value  state value
 */
dvt.AdfDiagram.prototype.setAutomationState = function(id, state, value) {
  if (!this._automationStatesMap)
    this._automationStatesMap = {};
  if (!this._automationStatesMap[id])
    this._automationStatesMap[id] = {};
  this._automationStatesMap[id][state] = value;
};

/**
 * Get the automation state value for the given id and state
 *
 * @param {string} id     component id
 * @param {string} state  state key
 * @return {object} state value
 */
dvt.AdfDiagram.prototype.getAutomationState = function(id, state) {
  if (this._automationStatesMap != null && this._automationStatesMap[id] != null)
    return this._automationStatesMap[id][state];
  return null;
};

/**
 * Clear the automation states
 */
dvt.AdfDiagram.prototype.clearAutomationState = function() {
  //Make sure interaction is enabled before clearing the states
  if (this._interactionDisableCounter == 0)
    this._automationStatesMap = null;
};

/**
 * @override
 */
dvt.AdfDiagram.prototype.renderNodeFromContext = function(nodeContext) {
  var node = this.getNodeById(nodeContext.getId());
  if (node.isDisclosed()) {
    //render all child nodes and apply layout context
    var layoutContext = this.CreateEmptyLayoutContext();
    var childNodes = nodeContext.getChildNodes();
    for (var i = 0; i < childNodes.length; i++) {
      var childNode = childNodes[i];
      if (!childNode.IsRendered)
        this.renderNodeFromContext(childNode);
      layoutContext.addNode(childNode);
    }
    this.ApplyLayoutContext(layoutContext);
    var containerPadding = nodeContext.getContainerPadding();
    if (containerPadding) {
      node.setContainerPadding(dvt.BaseDiagram.getContainerPadding(containerPadding));
    }
  }
  //render the node and update its layout context
  var afContext = this.createAfContext();
  node.createNodeContents(afContext);
  node.renderNode(afContext);
  this.UpdateNodeLayoutContext(nodeContext, node);

  //now when node is rendered - update child offset for container nodes
  //which is used to calculate global position for cross-container links
  if (node.isDisclosed()) {
    var childNodes = nodeContext.getChildNodes();
    for (var i = 0; i < childNodes.length; i++) {
      var childNode = childNodes[i];
      var relNodePos = new dvt.DiagramPoint(0, 0);
      var cnpPos = node.GetChildNodePanePosition();
      if (cnpPos) {
        var cPad = node.getContainerPadding();
        relNodePos.x += cnpPos.x - cPad.left;
        relNodePos.y += cnpPos.y - cPad.top;
      }
      var origOffset = childNode.ContentOffset;
      childNode.ContentOffset = new dvt.DiagramPoint(origOffset.x + relNodePos.x, origOffset.y + relNodePos.y);
    }
  }
};

/**
 * Adds all necessary nodes for cross container links as read only nodes to the layout context
 * @param {DvtDiagramLayoutContext} layoutContext layout context for the diagram
 * @param {string} groupId group id for level
 * @param {array} nodes array of nodes - the start or end node for teh link and all its parents
 * @param {dvt.Animator} animator optional animator
 * @private
 */
dvt.AdfDiagram.prototype._addCrossContainerNodes = function(layoutContext, groupId, nodes, animator) {
  var layout = layoutContext.getLayout();
  for (var j = nodes.length - 1; j >= 0; j--)
  {
    var nodeId = nodes[j];
    var node = this.getNodeById(nodeId);
    var nodeGroupId = node.getData().getGroupId();

    if (!nodeGroupId || nodeGroupId == groupId) {
      break;
    }

    var nodeContext = this.CreateLayoutContextNode(node, layout, false, layoutContext);
    nodeContext.setReadOnly(true);
    nodeContext.setContainerId(nodeGroupId);

    var relNodePos = new dvt.DiagramPoint(node.getTranslateX(), node.getTranslateY());
    if (animator) {
      var atx = animator.getDestVal(node, node.getTranslateX);
      var aty = animator.getDestVal(node, node.getTranslateY);
      if (atx != null && !isNaN(atx) && aty != null && !isNaN(aty)) {
        relNodePos = new dvt.DiagramPoint(atx, aty);
      }
    }
    //need to offset child node coords by height of container header
    var containerNode = this.getNodeById(nodeGroupId);
    //Bug 16383801: take translate of childNodePane of
    //parent container into account, which also incorporates
    //container header sizes and container padding
    var cnpPos = containerNode.GetChildNodePanePosition(animator);
    if (cnpPos) {
      //need to subtract out container padding, because the layout
      //may change that and we need to be able to update it dynamically
      var cPad = containerNode.getContainerPadding();
      relNodePos.x += cnpPos.x - cPad.left;
      relNodePos.y += cnpPos.y - cPad.top;
    }
    nodeContext.setPosition(relNodePos);
    //Add a node to the lookup map. This is done to support cross-container links by the layout.
    //The nodes should be treated as read-only nodes, since layout context will node be applied to them.
    layoutContext.addNodeToMap(nodeContext);
  }
};


/**
 * Return the Palette
 * @return {dvt.Palette}  the palette of the diagram
 */
dvt.AdfDiagram.prototype.getPalette = function() {
  return this._palette;
};
/**
 * @override
 */
dvt.AdfDiagram.prototype.GetMinZoomFactor = function() {
  return 0.5;
};

/**
 * @override
 */
dvt.AdfDiagram.prototype.GetLinkTranslationOffset = function(link, lc, lopt, animator) {
  var offset = new dvt.Point(0, 0);
  if (this._layoutBehavior !== 'global' || !link.getGroupId()) {
    offset = lopt;
  }
  else if (link.getGroupId() != lc.getCoordinateSpace()) {
    //link position is given either global or relative to some ancestor container
    var ancestorId = lc.getCoordinateSpace();
    var node = this.getNodeById(link.getGroupId());
    while (node) {
      var childPos = node.GetChildNodePanePosition(); // different from padding, it also includes facets
      offset['x'] -= childPos.x - node.GetPosition(animator)['x'];
      offset['y'] -= childPos.y - node.GetPosition(animator)['y'];
      var containerId = node.getGroupId();
      node = containerId != ancestorId ? this.getNodeById(containerId) : null;
    }
    if (!ancestorId) { //should adjust for top level offset that is already added to the top level nodes
      offset['x'] += lopt.x;
      offset['y'] += lopt.y;
    }
  }
  else {}//position already adjusted to the common container - nothing to adjust
  return offset;
};

/**
 * @protected
 * Updated Keyboard focus for diagram
 */
dvt.AdfDiagram.prototype.UpdateKeyboardFocus = function() {
  var focusObj = null;
  var focusObjOrig = this.getEventManager().getFocus();
  if (focusObjOrig instanceof dvt.AdfDiagramNode || focusObjOrig instanceof dvt.AdfDiagramLink) {
    var focucedObjId = focusObjOrig.getData().getId();
    focusObj = (focusObjOrig instanceof dvt.AdfDiagramNode) ? this.getNodeById(focucedObjId) :
        this.getLinkById(focucedObjId);
    if (focusObjOrig == focusObj) {
      return; // object is not stale - do nothing
    }
    if (focusObjOrig instanceof dvt.AdfDiagramNode)
      focusObjOrig.setFlyOutEnabled(false); // disable toolbar for the stale node
  }

  // if object is not visible or it is a hidden ancestor node, set it to null
  if ((focusObj && !focusObj.getVisible()) ||
      (focusObj && focusObj.getVisible() && focusObj.isHiddenAncestor && focusObj.isHiddenAncestor())) {
    focusObj = null;
  }

  if (!focusObj) {
    focusObj = this.GetInitialFocusNode();
  }

  this.getEventManager().setFocusObj(focusObj);
};

/**
 * Finds first visible, not hidden ancestor node to set initial focus
 * @return {dvt.AdfDiagramNode} node to set initial focus
 * @protected
 */
dvt.AdfDiagram.prototype.GetInitialFocusNode = function() {

  // helper function that performs breadth first search on nodes hierarchy
  var findFocusNode = function(diagram, level) {
    if (!dvt.ArrayUtils.isArray(level) || level.length === 0)
      return null;

    var nextLevel = [];
    for (var i = 0; i < level.length; i++) {
      var node = level[i];
      if (node.getVisible() && !node.isHiddenAncestor()) {
        return node;
      }
      //populate next level
      var count = node.getChildNodeCount();
      var childNodeIds = node.getChildNodeIds();
      for (var j = 0; j < count; j++) {
        nextLevel.push(diagram.getNodeById(childNodeIds[j]));
      }
    }
    return findFocusNode(diagram, nextLevel);
  };

  var nodesByGroupDepth = this.getSortedVisibleNodesByGroupDepth();
  var focusObj = nodesByGroupDepth && nodesByGroupDepth[0] ? findFocusNode(this, nodesByGroupDepth[0]) : null;
  return focusObj;
};




dvt.Bundle.addDefaultStrings(dvt.Bundle.ADF_DIAGRAM_PREFIX, {
  'STATE_STACKED': 'Stacked',
  'STATE_PREVIEW_MODE': 'Preview Mode',
  'STATE_HAS_ADDITIONAL_LINKS': 'Has Additional Links',
  'STATE_DRILLED': 'Drilled',
  'SIZE': 'Size',
  'PROMOTED_LINK_ARIA_DESC': 'Indirect'
});


// Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
/**
 * @constructor
 * @param {dvt.AdfDiagram} diagram The component for this container badge handler
 * @param {dvt.AdfDiagramNode} node The node for this container badge handler
 * @param {dvt.Button} button The button displable for this container badge handler
 */
var DvtAdfDiagramContainerBadgeHandler = function(diagram, node, button)
{
  this.Init(diagram, node, button);
};

dvt.Obj.createSubclass(DvtAdfDiagramContainerBadgeHandler, dvt.Obj, 'DvtAdfDiagramContainerBadgeHandler');

/**
 * @param {dvt.AdfDiagram} diagram The component for this container badge handler
 * @param {dvt.AdfDiagramNode} node The node for this container badge handler
 * @param {dvt.Button} button The button displable for this container badge handler
 * @protected
 */
DvtAdfDiagramContainerBadgeHandler.prototype.Init = function(diagram, node, button)
{
  this._diagram = diagram;
  this._node = node;
  this._button = button;
  this._button.setTooltip(this._diagram.getResourcesMap()[this._node.isDisclosed() ? 'discloseExpandedTip' : 'discloseCollapsedTip']);
};

DvtAdfDiagramContainerBadgeHandler.prototype.getTooltip = function()
{
  return this._button.getTooltip();
};

DvtAdfDiagramContainerBadgeHandler.prototype.HandleClick = function(event)
{
  //Bug 13381888: consume the event
  dvt.EventManager.consumeEvent(event);
  var nodeId = this._node.getData().getId();
  this._diagram.setNodeDisclosed(nodeId, !this._node.isDisclosed());
};

//Bug 13381888: clicking on container badge shouldn't affect selection
DvtAdfDiagramContainerBadgeHandler.prototype.isUnrelatedToSelection = function()
{
  return true;
};
var DvtAdfDiagramDataAnimationHandler = function(context) {
  this.Init(context, null);
};

dvt.Obj.createSubclass(DvtAdfDiagramDataAnimationHandler, dvt.DataAnimationHandler, 'DvtAdfDiagramDataAnimationHandler');

// TODO Document the expected interface for animatable objects:
// AnimateUpdate(DvtAdfDiagramDataAnimationHandler, oldObj)
// AnimateInsert(DvtAdfDiagramDataAnimationHandler)
// AnimateDelete(DvtAdfDiagramDataAnimationHandler)

DvtAdfDiagramDataAnimationHandler.PHASE_DELETE = 0;
DvtAdfDiagramDataAnimationHandler.PHASE_UPDATE = 1;
DvtAdfDiagramDataAnimationHandler.PHASE_INSERT = 2;

DvtAdfDiagramDataAnimationHandler.prototype.Init = function(context, deleteContainer) {
  DvtAdfDiagramDataAnimationHandler.superclass.Init.call(this, context, deleteContainer);

  //these lists are created at the beginning of ConstructAnimation and
  //are only valid for the duration of that call
  this._oldNodes = null;
  this._newNodes = null;
  this._oldLinks = null;
  this._newLinks = null;
  this._skipNodes = null;
  this._skipLinks = null;
};

DvtAdfDiagramDataAnimationHandler.prototype.ConstructAnimation = function(oldDiagramState, newDiagram, oldNodes, newNodes, oldLinks, newLinks,
    oldArNodeIds, newArNodeIds, oldArLinkIds, newArLinkIds) {
  // If offscreen, no animation necessary
  if (this.getCtx().isOffscreen())
    return;

  if (oldDiagramState && newDiagram) {
    newDiagram.AnimateUpdate(this, oldDiagramState);
  }

  // Copy the object lists, since we will modify them
  var tmpNewNodes = {};
  for (var nodeId in newNodes) {
    tmpNewNodes[nodeId] = newNodes[nodeId];
  }
  newNodes = tmpNewNodes;
  var tmpNewLinks = {};
  for (var linkId in newLinks) {
    tmpNewLinks[linkId] = newLinks[linkId];
  }
  newLinks = tmpNewLinks;

  //temporarily save lists for the duration of this function
  var tmpOldNodes = {};
  for (var nodeId in oldNodes) {
    tmpOldNodes[nodeId] = oldNodes[nodeId];
  }
  oldNodes = tmpOldNodes;
  var tmpOldLinks = {};
  for (var linkId in oldLinks) {
    tmpOldLinks[linkId] = oldLinks[linkId];
  }
  oldLinks = tmpOldLinks;
  this._oldNodes = oldNodes;
  this._newNodes = newNodes;
  this._oldLinks = oldLinks;
  this._newLinks = newLinks;
  this._skipNodes = {};
  this._skipLinks = {};

  // Loop through the two lists and diff the changes.
  // Note: This implementation considers changes in order to be updates.
  if (oldNodes) {
    //Bug 16493799: can't loop over map because order may be different in Flash, so
    //loop over array instead
    for (var nodeIndex = 0; nodeIndex < oldArNodeIds.length; nodeIndex++) {
      var oldNodeId = oldArNodeIds[nodeIndex];

      //check whether we need to skip this node
      if (this._skipNodes[oldNodeId]) {
        delete oldNodes[oldNodeId];
        delete newNodes[oldNodeId];
        continue;
      }

      var oldNode = oldNodes[oldNodeId];
      if (!oldNode) { // oldItem must exist for update or delete
        continue;
      }

      // Loop through the new list looking for a match
      var newNode = newNodes[oldNodeId];
      if (newNode) { // newItem must exist for update
        // Match found, remove the item from the new list since it's handled
        newNode.AnimateUpdate(this, oldNode);
        delete newNodes[oldNodeId];
      }
      // If no match found, it was a delete.
      else {
        oldNode.AnimateDelete(this, newDiagram);
      }
    }
  }

  // All remaining objects in newNodes are inserts
  //Bug 16493799: can't loop over map because order may be different in Flash, so
  //loop over array instead
  var tmpNewArNodeIds = [];
  for (var nodeIndex = 0; nodeIndex < newArNodeIds.length; nodeIndex++) {
    var nodeId = newArNodeIds[nodeIndex];
    if (newNodes[nodeId]) {
      tmpNewArNodeIds.push(nodeId);
    }
  }
  for (var nodeIndex = 0; nodeIndex < tmpNewArNodeIds.length; nodeIndex++) {
    var nodeId = tmpNewArNodeIds[nodeIndex];

    //check whether we need to skip this node
    if (this._skipNodes[nodeId]) {
      delete oldNodes[nodeId];
      delete newNodes[nodeId];
      continue;
    }

    if (newNodes[nodeId]) { // must be valid object for insert
      newNodes[nodeId].AnimateInsert(this);
    }
  }

  // Loop through the two lists and diff the changes.
  // Note: This implementation considers changes in order to be updates.
  if (oldLinks) {
    for (var oldLinkId in oldLinks) {
      //check whether we need to skip this link
      if (this._skipLinks[oldLinkId]) {
        delete oldLinks[oldLinkId];
        delete newLinks[oldLinkId];
        continue;
      }

      var oldLink = oldLinks[oldLinkId];
      if (!oldLink) { // oldItem must exist for update or delete
        continue;
      }

      // Loop through the new list looking for a match
      var newLink = newLinks[oldLinkId];
      if (newLink) { // newItem must exist for update
        // Match found, remove the item from the new list since it's handled
        newLink.AnimateUpdate(this, oldLink);
        delete newLinks[oldLinkId];
      }
      // If no match found, it was a delete.
      else {
        oldLink.AnimateDelete(this, newDiagram);
      }
    }
  }

  // All remaining objects in newLinks are inserts
  for (var linkId in newLinks) {
    //check whether we need to skip this link
    if (this._skipLinks[linkId]) {
      delete oldLinks[linkId];
      delete newLinks[linkId];
      continue;
    }

    if (newLinks[linkId]) { // must be valid object for insert
      newLinks[linkId].AnimateInsert(this);
    }
  }

  //clear saved lists
  this._oldNodes = null;
  this._newNodes = null;
  this._oldLinks = null;
  this._newLinks = null;
  this._skipNodes = null;
  this._skipLinks = null;
};


/**
 * Finds node descendants to skip
 * @param {string} ancestorNodeId Node id
 * @param {dvt.AdfDiagram} diagram The diagram component that has nodes to process
 * @param {number} skipLevels Number of levels to skip
 */
DvtAdfDiagramDataAnimationHandler.prototype.SkipDescendants = function(ancestorNodeId, diagram, skipLevels) {
  if (!skipLevels || skipLevels < 0) {
    skipLevels = 0;
  }
  var node = diagram.getNodeById(ancestorNodeId);
  if (!node)
    return;

  var childNodeIds = node.getChildNodeIds();
  if (childNodeIds) {
    for (var i = 0; i < childNodeIds.length; i++) {
      var childNodeId = childNodeIds[i];
      if (skipLevels < 1) {
        this._skipNodes[childNodeId] = true;
      }
      this.SkipDescendants(childNodeId, diagram, skipLevels - 1);
    }
  }
  if (skipLevels < 1) {
    var childLinkIds = node.getChildLinkIds();
    if (childLinkIds) {
      for (var i = 0; i < childLinkIds.length; i++) {
        var childLinkId = childLinkIds[i];
        this._skipLinks[childLinkId] = true;
      }
    }
    var crossChildLinkIds = node.getCrossChildLinkIds();
    if (crossChildLinkIds) {
      for (var i = 0; i < crossChildLinkIds.length; i++) {
        var crossChildLinkId = crossChildLinkIds[i];
        this._skipLinks[crossChildLinkId] = true;
      }
    }
  }
};
// Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
/**
 * @extends {dvt.Container}
 * @constructor
 * @class DvtAdfDiagramFlyOutButton Flyout or Action button used on Diagram toolbar
 * @param {dvt.Context} context Platform specific context object
 * @param {string} iconEna URL to the button image in enabled state
 * @param {string} iconOvr URL to the button image in hover state
 * @param {string} iconDwn URL to the button image in pressed down state
 * @param {string} tooltip Button tooltip
 * @param {{dvt.EventManager} eventManager The dvt.EventManager for this component}
 * @param {boolean} bRoundTopLeft True if top left corner should be rounded
 * @param {boolean} bRoundTopRight True if top right corner should be rounded
 * @param {boolean} bRoundBottomLeft True if bottom left corner should be rounded
 * @param {boolean} bRoundBottomRight True if bottom right corner should be rounded
 * @param {object} styleMap The object containing style specifications for this component
 * @param {string} id Optional id for this object
 * @param {function} callback The optional function that should be called on click event
 * @param {object} callbackObj The optional object instance on which the callback function is defined
 */
var DvtAdfDiagramFlyOutButton = function(context, iconEna, iconOvr, iconDwn, tooltip, eventManager, 
    bRoundTopLeft, bRoundTopRight, bRoundBottomLeft, bRoundBottomRight, 
    styleMap, id, callback, callbackObj)
{
  this.Init(context, iconEna, iconOvr, iconDwn, tooltip, eventManager,
            bRoundTopLeft, bRoundTopRight, bRoundBottomLeft, bRoundBottomRight,
            styleMap, id, callback, callbackObj);
};

dvt.Obj.createSubclass(DvtAdfDiagramFlyOutButton, dvt.Container, 'DvtAdfDiagramFlyOutButton');

DvtAdfDiagramFlyOutButton._BUTTON_WIDTH = 44;
DvtAdfDiagramFlyOutButton._BUTTON_HEIGHT = 44;
DvtAdfDiagramFlyOutButton._ICON_WIDTH = 24;
DvtAdfDiagramFlyOutButton._ICON_HEIGHT = 24;


/**
 * Initialization method called by the constructor
 * @param {dvt.Context} context Platform specific context object
 * @param {string} iconEna URL to the button image in enabled state
 * @param {string} iconOvr URL to the button image in hover state
 * @param {string} iconDwn URL to the button image in pressed down state
 * @param {string} tooltip Button tooltip
 * @param {{dvt.EventManager} eventManager The dvt.EventManager for this component}
 * @param {boolean} bRoundTopLeft True if top left corner should be rounded
 * @param {boolean} bRoundTopRight True if top right corner should be rounded
 * @param {boolean} bRoundBottomLeft True if bottom left corner should be rounded
 * @param {boolean} bRoundBottomRight True if bottom right corner should be rounded
 * @param {object} styleMap The object containing style specifications for this component
 * @param {string} id Optional id for this object
 * @param {function} callback The optional function that should be called on click event
 * @param {object} callbackObj The optional object instance on which the callback function is defined
 */
DvtAdfDiagramFlyOutButton.prototype.Init = function(context, iconEna, iconOvr, iconDwn, tooltip, eventManager, 
    bRoundTopLeft, bRoundTopRight, bRoundBottomLeft, bRoundBottomRight, 
    styleMap, id, callback, callbackObj)
{
  DvtAdfDiagramFlyOutButton.superclass.Init.call(this, context, null, id);

  this._tooltip = tooltip;
  this.RenderSelf(context, iconEna, iconOvr, iconDwn, bRoundTopLeft, bRoundTopRight, bRoundBottomLeft, bRoundBottomRight, styleMap, callback, callbackObj);

  if (eventManager) {
    eventManager.associate(this, this);
  }
};

DvtAdfDiagramFlyOutButton.prototype.getTooltip = function()
{
  if (this._tooltip) {
    return this._tooltip;
  }
  return null;
};

DvtAdfDiagramFlyOutButton.prototype.getWidth = function() {
  return DvtAdfDiagramFlyOutButton._BUTTON_WIDTH;
};

DvtAdfDiagramFlyOutButton.prototype.getHeight = function() {
  return DvtAdfDiagramFlyOutButton._BUTTON_HEIGHT;
};

/**
 * Get flyout button
 * @return {dvt.Button}
 */
DvtAdfDiagramFlyOutButton.prototype.getButton = function() {
  return this._button;
};

/**
 * Creates a flyout button
 * @param {dvt.Context} context Platform specific context object
 * @param {string} iconEna URL to the button image in enabled state
 * @param {string} iconOvr URL to the button image in hover state
 * @param {string} iconDwn URL to the button image in pressed down state
 * @param {boolean} bRoundTopLeft True if top left corner should be rounded
 * @param {boolean} bRoundTopRight True if top right corner should be rounded
 * @param {boolean} bRoundBottomLeft True if bottom left corner should be rounded
 * @param {boolean} bRoundBottomRight True if bottom right corner should be rounded
 * @param {object} styleMap The object containing style specifications for this component
 * @param {function} callback The optional function that should be called on click event
 * @param {object} callbackObj The optional object instance on which the callback function is defined
 */
DvtAdfDiagramFlyOutButton.prototype.RenderSelf = function(context, iconEna, iconOvr, iconDwn, 
    bRoundTopLeft, bRoundTopRight, bRoundBottomLeft, bRoundBottomRight,
    styleMap, callback, callbackObj) {

  var ww = this.getWidth();
  var hh = this.getHeight();

  var iconWidth = DvtAdfDiagramFlyOutButton._ICON_WIDTH;
  var iconHeight = DvtAdfDiagramFlyOutButton._ICON_WIDTH;
  var iconX = .5 * (ww - iconWidth);
  var iconY = .5 * (hh - iconHeight);

  var buttonFillEna = dvt.AdfDiagram.getButtonFill(styleMap, dvt.AdfDiagram.BUTTON_STATE_ENA);
  var buttonFillOvr = dvt.AdfDiagram.getButtonFill(styleMap, dvt.AdfDiagram.BUTTON_STATE_OVR);
  var buttonFillDwn = dvt.AdfDiagram.getButtonFill(styleMap, dvt.AdfDiagram.BUTTON_STATE_DWN);

  var buttonStrokeEna = dvt.AdfDiagram.getButtonStroke(styleMap, dvt.AdfDiagram.BUTTON_STATE_ENA);
  var buttonStrokeOvr = dvt.AdfDiagram.getButtonStroke(styleMap, dvt.AdfDiagram.BUTTON_STATE_OVR);
  var buttonStrokeDwn = dvt.AdfDiagram.getButtonStroke(styleMap, dvt.AdfDiagram.BUTTON_STATE_DWN);

  var rx = dvt.AdfDiagram.getButtonBorderRadius(styleMap);

  var arPath = this.GetPathArray(ww, hh, rx, bRoundTopLeft, bRoundTopRight, bRoundBottomLeft, bRoundBottomRight);

  var upState = new dvt.Path(context, arPath);
  upState.setFill(buttonFillEna);
  upState.setStroke(buttonStrokeEna);
  var icon = new dvt.Image(context, iconEna, iconX, iconY, iconWidth, iconHeight);
  upState.addChild(icon);

  var overState = new dvt.Path(context, arPath);
  overState.setFill(buttonFillOvr);
  overState.setStroke(buttonStrokeOvr);
  icon = new dvt.Image(context, iconOvr, iconX, iconY, iconWidth, iconHeight);
  overState.addChild(icon);

  var downState = new dvt.Path(context, arPath);
  downState.setFill(buttonFillDwn);
  downState.setStroke(buttonStrokeDwn);
  icon = new dvt.Image(context, iconDwn, iconX, iconY, iconWidth, iconHeight);
  downState.addChild(icon);

  var button = new dvt.Button(context, upState, overState, downState, null, null, callback, callbackObj);
  button.setPixelHinting(true);

  this.addChild(button);
  this._button = button;
};

DvtAdfDiagramFlyOutButton.prototype.GetPathArray = function(ww, hh, cornerRadius, bRoundTopLeft, bRoundTopRight, bRoundBottomLeft, bRoundBottomRight) {
  var arPath = [];

  //top edge
  arPath.push('M', cornerRadius, 0);
  arPath.push('L', ww - cornerRadius, 0);

  //top right corner
  if (bRoundTopRight) {
    //arPath.push("Q", ww, 0, ww, cornerRadius);
    arPath.push('A', cornerRadius, cornerRadius, 0, 0, 1, ww, cornerRadius);
  }
  else {
    arPath.push('L', ww, 0, 'L', ww, cornerRadius);
  }

  //right edge
  arPath.push('L', ww, hh - cornerRadius);

  //bottom right corner
  if (bRoundBottomRight) {
    //arPath.push("Q", ww, hh, ww - cornerRadius, hh);
    arPath.push('A', cornerRadius, cornerRadius, 0, 0, 1, ww - cornerRadius, hh);
  }
  else {
    arPath.push('L', ww, hh, 'L', ww - cornerRadius, hh);
  }

  //bottom edge
  arPath.push('L', cornerRadius, hh);

  //bottom left corner
  if (bRoundBottomLeft) {
    //arPath.push("Q", 0, hh, 0, hh - cornerRadius);
    arPath.push('A', cornerRadius, cornerRadius, 0, 0, 1, 0, hh - cornerRadius);
  }
  else {
    arPath.push('L', 0, hh, 'L', 0, hh - cornerRadius);
  }

  //left edge
  arPath.push('L', 0, cornerRadius);

  //top left corner
  if (bRoundTopLeft) {
    //arPath.push("Q", 0, 0, cornerRadius, 0);
    arPath.push('A', cornerRadius, cornerRadius, 0, 0, 1, cornerRadius, 0);
  }
  else {
    arPath.push('L', 0, 0, 'L', cornerRadius, 0);
  }

  return arPath;
};
// Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
/**
  * Class representing an animation to fade in an object.
  * @extends {dvt.AnimFadeIn}
  * @class DvtAdfDiagramLinkAnimFadeIn
  * @constructor
  *
  * @param {dvt.Context}  context  platform specific context object
  * @param objs  an Array of DvtAdfDiagramLinks to animate
  * @param {number}  duration  length of animation, in seconds
  * @param {number}  delay  time to delay start of animation, in seconds
  */
var DvtAdfDiagramLinkAnimFadeIn = function(context, objs, duration, delay) {
  this.Init(context, objs, duration, delay);
};

if (typeof dvt.AnimFadeIn != 'undefined') {
  // dvt.AnimFadeIn does not get pulled in for PNG
  dvt.Obj.createSubclass(DvtAdfDiagramLinkAnimFadeIn, dvt.AnimFadeIn, 'DvtAdfDiagramLinkAnimFadeIn');
}


/**
 * @protected
 * @override
 */
DvtAdfDiagramLinkAnimFadeIn.prototype.Init = function(context, objs, duration, delay) {
  DvtAdfDiagramLinkAnimFadeIn.superclass.Init.call(this, context, objs, duration, delay);
};


/**
 * @protected
 * @override
 */
DvtAdfDiagramLinkAnimFadeIn.prototype.InitEndState = function(obj) {
  this._animator.addProp(dvt.Animator.TYPE_NUMBER, obj, obj.getAlpha, obj.setAlpha, Math.min(1, obj.GetMaxAlpha()));
};
// Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
/**
 * @constructor
 * A class that handles initial animation for the diagram component
 * @param {dvt.Context} context The platform specific context object
 * @param {dvt.AdfDiagram} view The diagram component
 */
dvt.AdfDiagramInitAnim = function(context, view)
{
  this.Init(context, view);
};

dvt.Obj.createSubclass(dvt.AdfDiagramInitAnim, dvt.Obj, 'dvt.AdfDiagramInitAnim');

/**
 * @protected
 * Initialization method called by the constructor
 * @param {dvt.Context} context The platform specific context object
 * @param {dvt.AdfDiagram} view The diagram component
 */
dvt.AdfDiagramInitAnim.prototype.Init = function(context, view)
{
  this._context = context;
  this._view = view;

  this._onEnd = null;
  this._onEndObj = null;
};

/**
 * @protected
 * Returns a function used by animator to enable diagram interaction at the end of the animation
 * @return {function} a function that enables interaction for the diagram
 */
dvt.AdfDiagramInitAnim.prototype.GetEnableInteractionFunc = function() {
  var viewRef = this._view;
  return function() {viewRef.setInteractionEnabled(true);};
};

/**
 * Plays the specified animation
 * @param {string} type Animation type
 */
dvt.AdfDiagramInitAnim.prototype.play = function(type)
{
  this._view.setInteractionEnabled(false);
  switch (type)
  {
    case dvt.BlackBoxAnimationHandler.ALPHA_FADE:
      this.PlayBlackBoxAnim(type);
      break;
    case dvt.AdfDiagram.INIT_ANIM_EXPLODE:
      this.PlayExplode();
      break;
    case dvt.AdfDiagram.INIT_ANIM_ELASTIC_EXPLODE:
      this.PlayExplode(true);
      break;
    case dvt.AdfDiagram.INIT_ANIM_GROW_LINKS:
      this.PlayGrowLinks();
      break;
    case dvt.AdfDiagram.INIT_ANIM_POP:
      this.PlayPop();
      break;
    case dvt.AdfDiagram.INIT_ANIM_EXPLODE_GROW_LINKS:
      this.PlayExplodeGrowLinks();
      break;
    case dvt.AdfDiagram.INIT_ANIM_ELASTIC_EXPLODE_GROW_LINKS:
      this.PlayExplodeGrowLinks(true);
      break;
    case dvt.AdfDiagram.INIT_ANIM_POP_GROW_LINKS:
      this.PlayPopGrowLinks();
      break;
  }
};

/**
 * Sets the function to call when this playable ends
 * @param {function}  onEnd  function to call when this playable ends
 * @param {object}  onEndObj  optional reference to object instance on which the
 *        onEnd function is defined
 */
dvt.AdfDiagramInitAnim.prototype.setOnEnd = function(onEnd, onEndObj)
{
  this._onEnd = onEnd;
  if (onEndObj)
  {
    this._onEndObj = onEndObj;
  }
  else
  {
    this._onEndObj = null;
  }
};

/**
 * Gets the function to call when this playable ends.
 * @return {array} an array of two elements:
 *         [0] the function
 *         [1] optional reference to object instance on which the function is defined
 */
dvt.AdfDiagramInitAnim.prototype.getOnEnd = function()
{
  return [this._onEnd, this._onEndObj];
};

/**
 * @protected
 * Plays alpha fade animation
 * @param {string} type Animation type
 */
dvt.AdfDiagramInitAnim.prototype.PlayBlackBoxAnim = function(type)
{
  var comp = this._view;
  //Bug 13723916: fade in the nodes and links, not the whole component
  if (this._view.getPanZoomCanvas() &&
      this._view.getPanZoomCanvas().getContentPane()) {
    comp = this._view.getPanZoomCanvas().getContentPane();
  }
  var anim = dvt.BlackBoxAnimationHandler.getInAnimation(this._context, type, comp, null, this._view.getAnimationDuration());

  if (this._onEnd)
  {
    dvt.Playable.appendOnEnd(anim, this._onEnd, this._onEndObj);
  }

  dvt.Playable.appendOnEnd(anim, this.GetEnableInteractionFunc());
  anim.play();
};

/**
 * @protected
 * Plays explode animation
 * @param {boolean} bElastic True to create elastic effect
 */
dvt.AdfDiagramInitAnim.prototype.PlayExplode = function(bElastic)
{
  this._view._nodesPane.setAlpha(0);

  var fadeInNodes = new dvt.AnimFadeIn(this._context, this._view._nodesPane, this._view.getAnimationDuration());
  var explodeNodes = this.CreateExplodeAnim(bElastic);
  var fadeInLinks = this.CreateFadeInLinksAnim();
  var seqPlayable = new dvt.SequentialPlayable(this._context, fadeInNodes, explodeNodes, fadeInLinks);

  if (this._onEnd)
  {
    dvt.Playable.appendOnEnd(seqPlayable, this._onEnd, this._onEndObj);
  }

  dvt.Playable.appendOnEnd(seqPlayable, this.GetEnableInteractionFunc());
  seqPlayable.play();
};

/**
 * @protected
 * Plays grow links animation
 */
dvt.AdfDiagramInitAnim.prototype.PlayGrowLinks = function()
{
  this._view._nodesPane.setAlpha(0);

  var growLinks = this.CreateGrowLinksAnim();

  var fadeInNodes = new dvt.AnimFadeIn(this._context, this._view._nodesPane, this._view.getAnimationDuration());
  var seqPlayable = new dvt.SequentialPlayable(this._context, fadeInNodes, growLinks);

  if (this._onEnd)
  {
    dvt.Playable.appendOnEnd(seqPlayable, this._onEnd, this._onEndObj);
  }

  dvt.Playable.appendOnEnd(seqPlayable, this.GetEnableInteractionFunc());
  seqPlayable.play();
};

/**
 * @protected
 * Plays pop animation
 */
dvt.AdfDiagramInitAnim.prototype.PlayPop = function()
{
  var popInNodes = this.CreatePopAnim();
  var fadeInLinks = this.CreateFadeInLinksAnim();
  var seqPlayable = new dvt.SequentialPlayable(this._context, popInNodes, fadeInLinks);

  if (this._onEnd)
  {
    dvt.Playable.appendOnEnd(seqPlayable, this._onEnd, this._onEndObj);
  }

  dvt.Playable.appendOnEnd(seqPlayable, this.GetEnableInteractionFunc());
  seqPlayable.play();
};

/**
 * @protected
 * Plays explode grow links animation
 * @param {boolean} bElastic True to create elastic effect
 */
dvt.AdfDiagramInitAnim.prototype.PlayExplodeGrowLinks = function(bElastic)
{
  this._view._nodesPane.setAlpha(0);

  var fadeInNodes = new dvt.AnimFadeIn(this._context, this._view._nodesPane, this._view.getAnimationDuration());
  var explodeNodes = this.CreateExplodeAnim(bElastic);
  var growLinks = this.CreateGrowLinksAnim();
  var seqPlayable = new dvt.SequentialPlayable(this._context, fadeInNodes, explodeNodes, growLinks);

  if (this._onEnd)
  {
    dvt.Playable.appendOnEnd(seqPlayable, this._onEnd, this._onEndObj);
  }

  dvt.Playable.appendOnEnd(seqPlayable, this.GetEnableInteractionFunc());
  seqPlayable.play();
};

/**
 * @protected
 * Plays pop grow links animation
 */
dvt.AdfDiagramInitAnim.prototype.PlayPopGrowLinks = function()
{
  var popInNodes = this.CreatePopAnim();
  var growLinks = this.CreateGrowLinksAnim();
  var seqPlayable = new dvt.SequentialPlayable(this._context, popInNodes, growLinks);

  if (this._onEnd)
  {
    dvt.Playable.appendOnEnd(seqPlayable, this._onEnd, this._onEndObj);
  }

  dvt.Playable.appendOnEnd(seqPlayable, this.GetEnableInteractionFunc());
  seqPlayable.play();
};

/**
 * @protected
 * Create explode animation
 * @param {boolean} bElastic  flag indicates whether elasic easing is enabled
 * @return {dvt.AnimMoveTo}  Move to animation
 */
dvt.AdfDiagramInitAnim.prototype.CreateExplodeAnim = function(bElastic)
{
  var nodes = this._view._rootNodes;
  var arNodes = [];
  var arPoints = [];
  var node;
  var tx;
  var ty;
  var minX = Number.MAX_VALUE;
  var minY = Number.MAX_VALUE;
  var maxX = Number.MIN_VALUE;
  var maxY = Number.MIN_VALUE;
  for (var i = 0; i < nodes.length; i++)
  {
    node = this._view.getNodeById(nodes[i]);
    if (node.getVisible())
    {
      arNodes.push(node);
      tx = node.getTranslateX();
      ty = node.getTranslateY();
      arPoints.push(new dvt.Point(tx, ty));
      if (tx < minX)
      {
        minX = tx;
      }
      if (ty < minY)
      {
        minY = ty;
      }
      if (tx > maxX)
      {
        maxX = tx;
      }
      if (ty > maxY)
      {
        maxY = ty;
      }
    }
  }
  var cx = minX + (maxX - minX) / 2;
  var cy = minY + (maxY - minY) / 2;
  for (var i = 0; i < nodes.length; i++)
  {
    node = this._view.getNodeById(nodes[i]);
    if (node.getVisible())
    {
      node.setTranslate(cx, cy);
    }
  }

  var explodeNodes = new dvt.AnimMoveTo(this._context, arNodes, arPoints, (bElastic ? 1.75 : 1) * this._view.getAnimationDuration());
  if (bElastic)
  {
    explodeNodes.setEasing(dvt.BaseAnimation.EASING_ELASTIC_OUT);
  }
  return explodeNodes;
};

/**
 * Create grow links animation
 * @return {dvt.ParallelPlayable} set of DvtPlayables that are played at the same time.
 * @protected
 */
dvt.AdfDiagramInitAnim.prototype.CreateGrowLinksAnim = function()
{
  var growLinks = new dvt.CustomAnimation(this._context, null, this._view.getAnimationDuration());
  var growAnim = growLinks.getAnimator();
  var fadeLinks = new dvt.CustomAnimation(this._context, null,
      .5 * growLinks.getDuration());
  var fadeAnim = fadeLinks.getAnimator();

  var links = this._view._links;
  var linkId;
  var link;
  var linkPoints;
  for (linkId in links)
  {
    link = this._view.getLinkById(linkId);
    if (link.getVisible())
    {
      linkPoints = link.getPoints();
      var growAnimType = dvt.Animator.TYPE_GROW_POLYLINE;
      if (isNaN(linkPoints[0]))
      {
        growAnimType = dvt.Animator.TYPE_GROW_PATH;
        //make sure that link has at least two points and non-zero length so
        //that connectors render correctly
        link.setPoints(['M', linkPoints[1], linkPoints[2], 'L', linkPoints[1] + .0001, linkPoints[2]]);
      }
      else
      {
        //make sure that link has at least two points and non-zero length so
        //that connectors render correctly
        link.setPoints([linkPoints[0], linkPoints[1], linkPoints[0] + .0001, linkPoints[1]]);
      }
      link.setAlpha(0);

      growAnim.addProp(growAnimType, link, link.getPoints, link.setPoints, linkPoints);
      fadeAnim.addProp(dvt.Animator.TYPE_NUMBER, link, link.getAlpha, link.setAlpha, Math.min(1, link.GetMaxAlpha()));
    }
  }

  var parallelPlayable = new dvt.ParallelPlayable(this._context, growLinks, fadeLinks);
  return parallelPlayable;
};

/**
 * @protected
 * Create popping animation
 * @return {dvt.CombinedAnimPopIn}  animation object
 */
dvt.AdfDiagramInitAnim.prototype.CreatePopAnim = function()
{
  var arNodes = [];
  for (var i = 0; i < this._view._rootNodes.length; i++)
  {
    var node = this._view.getNodeById(this._view._rootNodes[i]);
    if (node.getVisible())
    {
      arNodes.push(node);
    }
  }

  var popInNodes = new dvt.CombinedAnimPopIn(this._context, arNodes, false, this._view.getAnimationDuration(), 2 * this._view.getAnimationDuration());
  return popInNodes;
};

/**
 * @protected
 * Get diagram links array
 * @return {array} array of links
 */
dvt.AdfDiagramInitAnim.prototype.GetLinksArray = function()
{
  var arLinks = [];
  for (var linkId in this._view._links)
  {
    var link = this._view.getLinkById(linkId);
    if (link.getVisible())
    {
      arLinks.push(link);
    }
  }
  return arLinks;
};

dvt.AdfDiagramInitAnim.prototype.CreateFadeInLinksAnim = function()
{
  var arLinks = this.GetLinksArray();
  //need to hide the links explicitly before the anim runs because
  //links in containers will become visible with their containing
  //nodes before this anim, and then will disappear and fade in
  //again when this anim runs in sequence
  for (var i = 0; i < arLinks.length; i++) {
    arLinks[i].setAlpha(0);
  }

  return new DvtAdfDiagramLinkAnimFadeIn(this._context, arLinks, this._view.getAnimationDuration());
};
/**
 * @constructor
 */
var DvtAdfDiagramLayoutCallback = function()
{
  this.Init();
};

dvt.Obj.createSubclass(DvtAdfDiagramLayoutCallback, dvt.Obj, 'DvtAdfDiagramLayoutCallback');

DvtAdfDiagramLayoutCallback.prototype.Init = function()
{
  this._layouts = {};
};

/**
 * Add layout
 * @param {String} name layout name
 * @param {function} func  pointer to layout function
 * @param {Object} obj  Object to pass in layout function
 */
DvtAdfDiagramLayoutCallback.prototype.addLayout = function(name, func, obj)
{
  this._layouts[name] = [func, obj];
};

DvtAdfDiagramLayoutCallback.prototype.removeLayout = function(name)
{
  delete this._layouts[name];
};

/**
 * Calls out to layout code
 * @param {dvt.AdfDiagram} diagram the diagram component
 * @param {DvtDiagramLayoutContext} layoutContext the layout context
 * @return {DvtDiagramLayoutContext} the returned layoutContext if the layout was successfully called, null otherwise
 */
DvtAdfDiagramLayoutCallback.prototype.doLayout = function(diagram, layoutContext) {
  var key = layoutContext.getLayout();
  var array = this._layouts[key];
  var func = array[0];
  var obj = array[1];

  if (func) {
    if (obj)
      func.call(obj, layoutContext);
    else
      func(layoutContext);
  }
  else {
    diagram.Log('Function for layout ' + key + ' is not defined', '2'); // SEVERE
    return null;
  }
  return layoutContext;
};
// Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
/**
 * @constructor
 */
var DvtAdfDiagramLayoutContainer = function(context, diagram, id)
{
  this.Init(context, diagram, id);
};

dvt.Obj.createSubclass(DvtAdfDiagramLayoutContainer, dvt.Container, 'DvtAdfDiagramLayoutContainer');

DvtAdfDiagramLayoutContainer.prototype.Init = function(context, diagram, id)
{
  DvtAdfDiagramLayoutContainer.superclass.Init.call(this, context, null, id);

  this._diagram = diagram;
  this._nodes = {};
};

DvtAdfDiagramLayoutContainer.prototype.addNode = function(node)
{
  this._nodes[node.getData().getId()] = node;
  this.addChild(node);
};

DvtAdfDiagramLayoutContainer.prototype.layout = function(animator)
{
  if (this._diagram.getLayoutCallback())
  {
    var layoutContext = this._diagram.CreateEmptyLayoutContext();
    layoutContext.setLayoutAttributes({padding: 10});

    var nodeId;
    var node;
    for (nodeId in this._nodes)
    {
      node = this._nodes[nodeId];
      layoutContext.addNode(this._diagram.CreateLayoutContextNode(node, null, false, layoutContext));
    }

    if (layoutContext.getNodeCount() > 0) {
      this._diagram.SortLayoutContext(layoutContext);
      dvt.DiagramLayoutUtils.gridLayout(layoutContext);
      this.ApplyLayoutContext(layoutContext, animator);
    }
  }
};

/**
 * Positions nodes, links and labels in the preview container
 * @param {DvtDiagramLayoutContext} layoutContext  diagram layout context
 * @param {dvt.Animator} animator Optional animator for the component that is used to animate transition from an old state to a new one
 * @protected
 */
DvtAdfDiagramLayoutContainer.prototype.ApplyLayoutContext = function(layoutContext, animator)
{
  for (var ni = 0; ni < layoutContext.getNodeCount(); ni++)
  {
    var nc = layoutContext.getNodeByIndex(ni);
    var node = this.getNodeById(nc.getId());
    var pos = nc.getPosition();
    if (pos)
    {
      node.SetPosition(pos.x, pos.y, animator);
    }
    var labelBounds = nc.getLabelBounds();
    var labelPos = nc.getLabelPosition();
    if (pos && labelBounds && labelPos)
    {
      // translate to make these relative to the node
      var relLabelPos = new dvt.Point(labelPos.x - pos.x,
          labelPos.y - pos.y);
      if (animator)
      {
        animator.addProp(dvt.Animator.TYPE_RECTANGLE, node,
                         node.getLabelBounds, node.setLabelBounds,
                         labelBounds);
        animator.addProp(dvt.Animator.TYPE_POINT, node,
                         node.getLabelPosition, node.setLabelPosition,
                         relLabelPos);
      }
      else
      {
        node.setLabelBounds(labelBounds);
        node.setLabelPosition(relLabelPos);
      }
    }
  }
  for (var li = 0; li < layoutContext.getLinkCount(); li++)
  {
    var lc = layoutContext.getLinkByIndex(li);
    var link = this.getLinkById(lc.getId());

    var points = lc.getPoints();
    if (points)
    {
      if (animator && link.getPoints())
      {
        var linkAnimType = dvt.Animator.TYPE_POLYLINE;
        if (points.length > 0 && isNaN(points[0]))
        {
          linkAnimType = dvt.Animator.TYPE_PATH;
        }
        animator.addProp(linkAnimType, link,
                         link.getPoints, link.setPoints, points);
      }
      else
      {
        link.setPoints(points);
      }
    }
    var labelBounds = lc.getLabelBounds();
    var labelPos = lc.getLabelPosition();
    if (labelBounds && labelPos)
    {
      if (animator)
      {
        animator.addProp(dvt.Animator.TYPE_RECTANGLE, link,
                         link.getLabelBounds, link.setLabelBounds,
                         labelBounds);
        animator.addProp(dvt.Animator.TYPE_POINT, link,
                         link.getLabelPosition, link.setLabelPosition,
                         labelPos);
      }
      else
      {
        link.setLabelBounds(labelBounds);
        link.setLabelPosition(labelPos);
      }
    }
  }
};

/**
 * Get node by id
 * @param {string} id  node id
 * @return {dvt.AdfDiagramNode}  diagram node
 */
DvtAdfDiagramLayoutContainer.prototype.getNodeById = function(id)
{
  var node = this._nodes[id];
  if (!node)
  {
    node = this._diagram.getNodeById(node);
  }
  return node;
};
/**
 * @constructor
 */
var DvtAdfDiagramLinkData = function()
{
  this.Init();
};

dvt.Obj.createSubclass(DvtAdfDiagramLinkData, dvt.Obj, 'DvtAdfDiagramLinkData');

DvtAdfDiagramLinkData.prototype.Init = function()
{
  this._id = null;
  this._groupId = null;
  this._templateId = null;
  this._startId = null;
  this._endId = null;
  this._elAttributes = [];
  this._layoutAttributes = null;
  this._rowIndex = -1;
};

DvtAdfDiagramLinkData.prototype.setId = function(id)
{
  this._id = id;
};

DvtAdfDiagramLinkData.prototype.getId = function()
{
  return this._id;
};

DvtAdfDiagramLinkData.prototype.setRowIndex = function(rowIndex) {
  this._rowIndex = rowIndex;
};

DvtAdfDiagramLinkData.prototype.getRowIndex = function() {
  return this._rowIndex;
};

DvtAdfDiagramLinkData.prototype.setGroupId = function(id)
{
  this._groupId = id;
};

DvtAdfDiagramLinkData.prototype.getGroupId = function()
{
  return this._groupId;
};

DvtAdfDiagramLinkData.prototype.setTemplateId = function(id)
{
  this._templateId = id;
};

DvtAdfDiagramLinkData.prototype.getTemplateId = function()
{
  return this._templateId;
};

DvtAdfDiagramLinkData.prototype.setStartId = function(id)
{
  this._startId = id;
};

DvtAdfDiagramLinkData.prototype.getStartId = function()
{
  return this._startId;
};

DvtAdfDiagramLinkData.prototype.setEndId = function(id)
{
  this._endId = id;
};

DvtAdfDiagramLinkData.prototype.getEndId = function()
{
  return this._endId;
};

DvtAdfDiagramLinkData.prototype.setElAttributes = function(attrs)
{
  this._elAttributes = attrs;
};

DvtAdfDiagramLinkData.prototype.getElAttributes = function()
{
  return this._elAttributes;
};

DvtAdfDiagramLinkData.prototype.setLayoutAttributes = function(layoutAttributes) {
  this._layoutAttributes = layoutAttributes;
};

DvtAdfDiagramLinkData.prototype.getLayoutAttributes = function() {
  return this._layoutAttributes;
};
// Copyright (c) 2011, 2020, Oracle and/or its affiliates. 

/**
 * @constructor
 */
dvt.AdfDiagramLink = function(context, linkData, diagram)
{
  this.Init(context, linkData, diagram);
};

dvt.Obj.createSubclass(dvt.AdfDiagramLink, dvt.BaseDiagramLink, 'dvt.AdfDiagramLink');

/**
 * Default link width
 * @const
 * @private
 */
dvt.AdfDiagramLink._DEFAULT_LINK_WIDTH = 1;
/**
 * Default link length used by palette
 * @const
 * @private
 */
dvt.AdfDiagramLink._DEFAULT_LINK_LENGTH = 28;

dvt.AdfDiagramLink._START_CONNECTOR = 0;
dvt.AdfDiagramLink._END_CONNECTOR = 1;

//Bug 15867125: padding to add to link width to make it easier to click
dvt.AdfDiagramLink._HIT_DETECTION_WIDTH_OFFSET = 4;

// keys for retrieving style properties


/**
 * Style key for the color of the link
 * @const
 */
dvt.AdfDiagramLink.COLOR = 'linkColor';


/**
 * Style key for the color of the selected link state
 * @const
 */
dvt.AdfDiagramLink.SELECTED_COLOR = 'linkSelectColor';


/**
 * Style key for the inner color of the hover link state
 * @const
 */
dvt.AdfDiagramLink.HOVER_INNER_COLOR = 'linkHoverInnerColor';


/**
 * Style key for the outer color of the hover link state
 * @const
 */
dvt.AdfDiagramLink.HOVER_OUTER_COLOR = 'linkHoverOuterColor';


/**
 * Style key for the color of promoted links
 * @const
 */
dvt.AdfDiagramLink.PROMOTED_COLOR = 'linkPromotedColor';


/**
 * Style key for the linkStyle of promoted links
 * @const
 */
dvt.AdfDiagramLink.PROMOTED_STYLE = 'linkPromotedStyle';


/**
 * Style key for the width of promoted links
 * @const
 */
dvt.AdfDiagramLink.PROMOTED_WIDTH = 'linkPromotedWidth';


/**
 * Style key for the link label style
 * @const
 */
dvt.AdfDiagramLink.LABEL_STYLE = 'linkLabelStyle';


/**
 * Initialization method called by the constructor
 *
 * @param {dvt.Context} context The rendering context
 * @param {DvtAdfDiagramLinkData} linkData the data for this Link
 * @param {dvt.AdfDiagram} diagram The Diagram component to which this link belongs
 */
dvt.AdfDiagramLink.prototype.Init = function(context, linkData, diagram)
{
  dvt.AdfDiagramLink.superclass.Init.call(this, context, linkData.getId(), diagram);

  this._data = linkData;

  this._linkDef;

  this._linkColor;
  this._linkWidth;
  this._linkType;
  this._linkStyle;
  this._shortDesc;
  this._endConnectorType;
  this._startConnectorType;
  this._label;
  this._bRendered = true;
  this._categories;

  this._initialPoints;
  this._hover = false;
  this._updateEnabled = false;
  this._deleteEnabled = false;

  this._dragItems = new Object();

  //these will be set from dvt.AdfDiagramNode.addChildLink or
  //dvt.AdfDiagramNode.addCrossChildLink
  this._linkParent;
  this._bCrossContainerLink = false;

  this._maxAlpha = 1.0;

  this._linkUnderlay;
  this._savedStroke;
  //Bug 15867125: underlay wider than link itself to make it easier
  //for mouse to interact with link
  this._hitDetectionUnderlay;

  this._bFocused = false;
  this._bFocusEffect = false;

  this._showPopupBehaviors;

  this._bPromoted = false;
  this._bDirty = false;

  //associate link with itself so that all children automatically
  //default to the link as the logical object
  this.GetDiagram().getEventManager().associate(this, this);

  //keyboard navigation
  this._keyboardNavNode;
  this._tabStopsConnectorArray = [];

  this._bFirefox = dvt.Agent.isPlatformGecko();
  this._bIE = (dvt.Agent.isPlatformIE());

  //skinning
  var linkStyles = this._diagram.getLinkStyles();
  this._defaultColor = linkStyles[dvt.AdfDiagramLink.COLOR];
  this._selectColor = linkStyles[dvt.AdfDiagramLink.SELECTED_COLOR];
  this._hoverInnerColor = linkStyles[dvt.AdfDiagramLink.HOVER_INNER_COLOR];
  this._hoverOuterColor = linkStyles[dvt.AdfDiagramLink.HOVER_OUTER_COLOR];
  this._linkLabelStyles = linkStyles[dvt.AdfDiagramLink.LABEL_STYLE];
  this._promotedColor = linkStyles[dvt.AdfDiagramLink.PROMOTED_COLOR];
  this._promotedStyle = linkStyles[dvt.AdfDiagramLink.PROMOTED_STYLE];
  this._promotedWidth = linkStyles[dvt.AdfDiagramLink.PROMOTED_WIDTH] ? linkStyles[dvt.AdfDiagramLink.PROMOTED_WIDTH] : dvt.AdfDiagramLink._DEFAULT_LINK_WIDTH;
};

/**
 * @override
 */
dvt.AdfDiagramLink.prototype.GetDiagram = function()
{
  if (this._diagram) {
    return this._diagram;
  }
  if (this._cachedDiagram) {
    return this._cachedDiagram;
  }
  var parent = this.getParent();
  while (parent)
  {
    if (parent instanceof dvt.AdfDiagram)
    {
      return parent;
    }
    parent = parent.getParent();
  }
};

/**
 * Returns bounds of link with stroke
 * @param {dvt.Animator} animator (optional) the animator into which this node should be rendered
 * @return {dvt.Rectangle} bounds The Diagram component to which this link belongs
 */
dvt.AdfDiagramLink.prototype.getDimensionsWithStroke = function(animator)
{
  var points = animator ? animator.getDestVal(this, this.getPoints) : this.getPoints();
  var dims = dvt.PathUtils.getDimensions(points);
  var width = this._hitDetectionUnderlay.getStroke().getWidth();
  dims.x -= width / 2;
  dims.y -= width / 2;
  dims.h += width;
  dims.w += width;
  if (this.getStartConnector()) {
    dims = this.getStartConnector().getDimensions(this).getUnion(dims);
  }
  if (this.getEndConnector()) {
    dims = this.getEndConnector().getDimensions(this).getUnion(dims);
  }
  return dims;
};


dvt.AdfDiagramLink.prototype.showHoverEffect = function() {
  this._showFeedback(true, this._selected);
};

dvt.AdfDiagramLink.prototype.hideHoverEffect = function() {
  if (this._bFocusEffect || this._selected) {
    this._showFeedback(this._bFocusEffect, this._selected); // keep hover effect if we're displaying keyboard focus
  }
  else if (this._bHighlight) {
    this._hideFeedback();
  }
};
/**
 * Show link roll over effect
 *
 * @param {dvt.MouseEvent | dvt.KeyboardEvent} event the event triggering the roll over
 */
dvt.AdfDiagramLink.prototype.showRollOverEffect = function(event) {
  //don't set flag here because when view is re-rendered when crossing
  //a zoom threshold, the node might show the hover effect by mistake
  //this._hover = true;
  this.GetDiagram().ShowLinkHoverEffect(this, event instanceof dvt.KeyboardEvent);
};
/**
 * Hide link roll over effect
 */
dvt.AdfDiagramLink.prototype.hideRollOverEffect = function() {
  //don't set flag here because when view is re-rendered when crossing
  //a zoom threshold, the node might lose the hover effect by mistake
  //this._hover = false;
  this.GetDiagram().HideLinkHoverEffect(this);
};


/**
 * Applies hover and selection feedback to the link underlay object
 *
 * @param {boolean} bHover whether or not hover feedback should be displayed
 * @param {boolean} bSelect whether or not selection feedback should be displayed
 */
dvt.AdfDiagramLink.prototype.ApplyStrokeToUnderlay = function(bHover, bSelect) {
  var color = bSelect ? this._selectColor : this._hoverOuterColor;

  var strokeOffset = 2;

  if (this._linkUnderlay) {
    var stroke = this._linkUnderlay.getStroke().clone();
    stroke.setColor(color);
    stroke.setWidth(strokeOffset + this.GetAppliedLinkWidth());
    this.ApplyLinkStyle(stroke, true);
    this._linkUnderlay.setStroke(stroke, strokeOffset);
  }
};

dvt.AdfDiagramLink.prototype.HandleClick = function(event) {
  this.GetDiagram().HandleLinkClick(this);
};


/**
 * Applies hover and selection feedback to the link
 * @param {boolean} bHover whether or not hover feedback should be displayed
 * @param {boolean} bSelect whether or not selection feedback should be displayed
 * @private
 */
dvt.AdfDiagramLink.prototype._showFeedback = function(bHover, bSelect) {
  this._bHighlight = true;
  this._preserveKeyboardFocusEffect = false;
  if (bHover) {
    if (!this._savedStroke) {
      this._savedStroke = this.getShape().getStroke();
    }
    var hoverStroke = this.getShape().getStroke().clone();
    if (hoverStroke.setColor) {
      hoverStroke.setColor(this._hoverInnerColor);
    }
    this.getShape().setStroke(hoverStroke);

    if (this.getStartConnector() && this.IsStandardConnector(dvt.AdfDiagramLink._START_CONNECTOR))
      this.ReplaceConnectorColor(this.getStartConnector(), hoverStroke);
    if (this.getEndConnector() && this.IsStandardConnector(dvt.AdfDiagramLink._END_CONNECTOR))
      this.ReplaceConnectorColor(this.getEndConnector(), hoverStroke);
  }
  else if (this._savedStroke) {
    this.getShape().setStroke(this._savedStroke);

    if (this.getStartConnector() && this.IsStandardConnector(dvt.AdfDiagramLink._START_CONNECTOR))
      this.ReplaceConnectorColor(this.getStartConnector(), this._savedStroke);
    if (this.getEndConnector() && this.IsStandardConnector(dvt.AdfDiagramLink._END_CONNECTOR))
      this.ReplaceConnectorColor(this.getEndConnector(), this._savedStroke);
    this._savedStroke = null;
  }

  //may need to create the underlay
  if (!this._linkUnderlay) {
    this._linkUnderlay = this.CreateFeedbackUnderlay();
    this.addChildAt(this._linkUnderlay, 0);
  }
  //change the stroke on the underlay
  this.ApplyStrokeToUnderlay(bHover, bSelect);

};


/**
 * Removes hover and selection feedback from the link
 * @private
 */
dvt.AdfDiagramLink.prototype._hideFeedback = function()
{
  this._bHighlight = false;

  if (this._savedStroke) {
    this.getShape().setStroke(this._savedStroke);

    if (this.getStartConnector() && this.IsStandardConnector(dvt.AdfDiagramLink._START_CONNECTOR))
      this.ReplaceConnectorColor(this.getStartConnector(), this._savedStroke);
    if (this.getEndConnector() && this.IsStandardConnector(dvt.AdfDiagramLink._END_CONNECTOR))
      this.ReplaceConnectorColor(this.getEndConnector(), this._savedStroke);

    this._savedStroke = null;
  }

  //may need to remove the underlay
  if (this._linkUnderlay) {// && !this._hover) {
    this.removeChild(this._linkUnderlay);
    if (this._linkUnderlay.destroy) {
      this._linkUnderlay.destroy();
    }
    this._linkUnderlay = null;
  }
  if (!this._preserveKeyboardFocusEffect)
    this._updateKeyboardFocusEffect(null);
};

/**
 * @override
 */
dvt.AdfDiagramLink.prototype.getDatatip = function() {
  return this.getShortDesc();
};

/**
 * Sets short description for the link
 * @param {string} shortDesc link short description
 */
dvt.AdfDiagramLink.prototype.setShortDesc = function(shortDesc) {
  this._shortDesc = shortDesc;
};

/**
 * Gets the link short description
 * @return {string} link short description
 */
dvt.AdfDiagramLink.prototype.getShortDesc = function() {
  if (this._bPromoted) {
    var format = null;
    if (this._promotionCount > 1) {
      format = this._diagram.getResourcesMap()['promotedLinksTip']; // plural
    }
    else if (!this._shortDesc) {
      format = this._diagram.getResourcesMap()['promotedLinkTip']; // singular
    }
    if (format) {
      return dvt.Bundle.format(format, [this._promotionCount]);
    }
  }
  return this._shortDesc;
};

dvt.AdfDiagramLink.prototype.getLayoutAttributes = function(layout) {
  var layoutAttrs = this.getData().getLayoutAttributes();
  if (layoutAttrs && layout) {
    return layoutAttrs[layout];
  }
  return {};
};

/**
 * @override
 */
dvt.AdfDiagramLink.prototype.setEndConnectorType = function(type) {
  this._endConnectorType = type;
};

/**
 * @override
 */
dvt.AdfDiagramLink.prototype.getEndConnectorType = function() {
  return this._endConnectorType;
};

/**
 * @override
 */
dvt.AdfDiagramLink.prototype.setStartConnectorType = function(type) {
  this._startConnectorType = type;
};

/**
 * @override
 */
dvt.AdfDiagramLink.prototype.getStartConnectorType = function() {
  return this._startConnectorType;
};

dvt.AdfDiagramLink.prototype.getData = function()
{
  return this._data;
};

dvt.AdfDiagramLink.prototype.setLinkDef = function(linkDef)
{
  this._linkDef = linkDef;

  if (linkDef)
  {
    linkDef.stamp(this.getData().getElAttributes(), this);
    this._refreshCursor();
  }
  this._bDirty = true;
};

dvt.AdfDiagramLink.prototype.getLinkDef = function()
{
  return this._linkDef;
};


/**
 * @override
 */
dvt.AdfDiagramLink.prototype.setLinkColor = function(lc)
{
  this._linkColor = lc;
};


/**
 * @override
 */
dvt.AdfDiagramLink.prototype.getLinkColor = function()
{
  if (this._linkColor)
  {
    return this._linkColor;
  }
  return this._defaultColor;
};


/**
 * Sets links width
 * @param {number} lw link width
 */
dvt.AdfDiagramLink.prototype.setLinkWidth = function(lw)
{
  this._linkWidth = lw;
};

/**
 * Gets link width
 * @return {number} link width
 */
dvt.AdfDiagramLink.prototype.getLinkWidth = function()
{
  if (this._linkWidth)
  {
    return this._linkWidth;
  }
  return dvt.AdfDiagramLink._DEFAULT_LINK_WIDTH;
};


/**
 * Sets links length
 * @param {number} ll link length
 */
dvt.AdfDiagramLink.prototype.setLinkLength = function(ll)
{
  this._linkLength = ll;
};


/**
 * Gets link length
 * @return {number} link length
 */
dvt.AdfDiagramLink.prototype.getLinkLength = function()
{
  if (this._linkLength)
  {
    return this._linkLength;
  }
  return dvt.AdfDiagramLink._DEFAULT_LINK_LENGTH;
};

dvt.AdfDiagramLink.prototype.setLinkType = function(lt)
{
  this._linkType = lt;
};

dvt.AdfDiagramLink.prototype.getLinkType = function()
{
  if (this._linkType)
  {
    return this._linkType;
  }
  return this.GetDefaultLinkType();
};


/**
 * @protected
 */
dvt.AdfDiagramLink.prototype.GetDefaultLinkType = function()
{
  return dvt.AdfDiagramLinkDef.LINK_TYPE_DIRECT;
};

/**
 * @override
 */
dvt.AdfDiagramLink.prototype.setLinkStyle = function(ls)
{
  this._linkStyle = ls;
};

/**
 * @override
 */
dvt.AdfDiagramLink.prototype.getLinkStyle = function()
{
  if (this._linkStyle)
  {
    return this._linkStyle;
  }
  return dvt.AdfDiagramLinkDef.LINK_STYLE_SOLID;
};

dvt.AdfDiagramLink.prototype.setLabel = function(label)
{
  this._label = label;
};

/**
 * Get the link label
 * @return {String}  Diagram link label
 */
dvt.AdfDiagramLink.prototype.getLabel = function()
{
  return this._label;
};

dvt.AdfDiagramLink.prototype.setRendered = function(bRendered)
{
  this._bRendered = bRendered;
};

dvt.AdfDiagramLink.prototype.isRendered = function()
{
  return this._bRendered;
};

dvt.AdfDiagramLink.prototype.setUpdateEnabled = function(enabled)
{
  this._updateEnabled = enabled;
};

dvt.AdfDiagramLink.prototype.isUpdateEnabled = function()
{
  return this._updateEnabled;
};

dvt.AdfDiagramLink.prototype.setDeleteEnabled = function(enabled)
{
  this._deleteEnabled = enabled;
};

dvt.AdfDiagramLink.prototype.isDeleteEnabled = function()
{
  return this._deleteEnabled;
};

dvt.AdfDiagramLink.prototype.DestroyContent = function() {
  //Bug 14813637: connectors are children of shape, so destroy them before destroying shape
  if (this.getEndConnector())
  {
    var ecParent = this.getEndConnector().getParent();
    if (ecParent) {
      ecParent.removeChild(this.getEndConnector());
    }
    if (this.getEndConnector().destroy)
    {
      this.getEndConnector().destroy();
    }
    this.setEndConnector(null);
  }
  if (this.getStartConnector())
  {
    var scParent = this.getStartConnector().getParent();
    if (scParent) {
      scParent.removeChild(this.getStartConnector());
    }
    if (this.getStartConnector().destroy)
    {
      this.getStartConnector().destroy();
    }
    this.setStartConnector(null);
  }
  if (this.getShape())
  {
    this.removeChild(this.getShape());
    if (this.getShape().destroy)
    {
      this.getShape().destroy();
    }
    this.setShape(null);
  }
  if (this._linkUnderlay) {
    this.removeChild(this._linkUnderlay);
    if (this._linkUnderlay.destroy) {
      this._linkUnderlay.destroy();
    }
    this._linkUnderlay = null;
  }
  if (this._hitDetectionUnderlay) {
    this.removeChild(this._hitDetectionUnderlay);
    if (this._hitDetectionUnderlay.destroy) {
      this._hitDetectionUnderlay.destroy();
    }
    this._hitDetectionUnderlay = null;
  }
  this._hover = false;
  this._savedStroke = null;
};


/**
 * Renders this link
 */
dvt.AdfDiagramLink.prototype.renderLink = function()
{
  if (this.getShape() && !this._bDirty) {
    return;  // No need to rerender if not dirty
  }
  this.DestroyContent();

  //save old label bounds so that we can apply them to a new label
  var oldLabelBounds = null;
  var oldLabelPos = null;
  var oldLabelRotAngle = null;
  var oldLabelRotPoint = null;
  var oldLabelAlpha = null;
  if (this._labelObj)
  {
    oldLabelBounds = this.getLabelBounds();
    oldLabelPos = this.getLabelPosition();
    oldLabelRotAngle = this.getLabelRotationAngle();
    oldLabelRotPoint = this.getLabelRotationPoint();
    oldLabelAlpha = this._labelObj.getAlpha();
    this.removeChild(this._labelObj);
    if (this._labelObj.destroy)
    {
      this._labelObj.destroy();
    }
    this._labelObj = null;
  }
  var bCanDrawArrow = true;
  var points = this._pathCmds;
  if (!points)
  {
    points = ['M', 0, 0, 'L', 0, 0];
    // bCanDrawArrow = false;
  }
  var id = this.getData().getId();
  this.setShape(new dvt.Path(this.getCtx(), points, id));
  this.getShape().setFill(null);
  //add shape underneath any custom connectors
  //Bug 14813637: add shape to link before creating connectors, so that when connectors are added to shape they
  //are on display list and we can get dimensions for them
  this.addChildAt(this.getShape(), 0);

  //this.GetDiagram().getEventManager().associate(this.getShape(), this);
  var linkColor = this.GetAppliedLinkColor();
  var linkWidth = this.GetAppliedLinkWidth();
  var stroke = new dvt.SolidStroke(linkColor, 1, linkWidth);
  // Bug 15868719 - use non-scaling stroke on the link on older devices with iOS and Safari
  if (dvt.Agent.isTouchDevice() && dvt.Agent.isBrowserSafari() && dvt.Agent.getDevicePixelRatio() == 1)
    stroke.setFixedWidth(true);
  this.ApplyLinkStyle(stroke);
  this.getShape().setStroke(stroke);

  if (bCanDrawArrow)
  {
    var endConnectorType = this.getEndConnectorType();
    var endConnectorTemplate = this.getConnectorTemplate(dvt.DiagramLinkConnectorUtils.END_CONNECTOR);
    this.setEndConnector(this.CreateConnector(this._points, endConnectorType, 1, endConnectorTemplate));
    var startConnectorType = this.getStartConnectorType();
    var startConnectorTemplate = this.getConnectorTemplate(dvt.DiagramLinkConnectorUtils.START_CONNECTOR);
    this.setStartConnector(this.CreateConnector(this._points, startConnectorType, 0, startConnectorTemplate));
  }

  this._refreshSelectionEffect(); //Maintain selection state through layout change

  if (!this._bPromoted && this.HasLabel())
  {
    this._labelObj = this.CreateLabel();
    this.addChild(this._labelObj);
    //initially apply old label bounds so that later layout transition
    //looks more natural
    if (oldLabelBounds)
    {
      this.setLabelBounds(oldLabelBounds);
    }
    if (oldLabelPos) {
      this.setLabelPosition(oldLabelPos);
    }
    if (oldLabelRotAngle != null) {
      this.setLabelRotationAngle(oldLabelRotAngle);
    }
    if (oldLabelRotPoint) {
      this.setLabelRotationPoint(oldLabelRotPoint);
    }
    if (oldLabelAlpha != null) { // need to explicitly check against null because 0 will be interpreted as false
      this._labelObj.setAlpha(oldLabelAlpha);
    }
  }

  //Bug 15867125: create a transparent underlay wider than the link
  //in order to make it easier to interact with the link
  if (!this._hitDetectionUnderlay) {
    this._hitDetectionUnderlay = this.CreateUnderlay('#000000', 0, dvt.AdfDiagramLink._HIT_DETECTION_WIDTH_OFFSET);
    this.addChildAt(this._hitDetectionUnderlay, 0);
  }

  this.addAccessibilityAttributes();
  this._bDirty = false;
};


/**
 * @override
 */
dvt.AdfDiagramLink.prototype.GetAppliedLinkColor = function() {
  return this._bPromoted ? this._promotedColor : this.getLinkColor();
};


/**
 * @override
 */
dvt.AdfDiagramLink.prototype.GetAppliedLinkStyle = function() {
  return this._bPromoted ? this._promotedStyle : this.getLinkStyle();
};


/**
 * @override
 */
dvt.AdfDiagramLink.prototype.GetAppliedLinkWidth = function() {
  return this._bPromoted ? this._promotedWidth : this.getLinkWidth();
};


/**
 * @override
 */
dvt.AdfDiagramLink.prototype.getStartConnectorOffset = function() {

  var debug = false;
  if (this.getStartConnector())
  {
    var stroke;

    var connectorTemplate = this.getConnectorTemplate(dvt.DiagramLinkConnectorUtils.START_CONNECTOR);
    if (!connectorTemplate)
    {
      stroke = this.getShape().getStroke().clone();
      stroke.setType(dvt.Stroke.SOLID);
    }
    if (debug) alert('is start Conn: get connector end offset');

    return dvt.DiagramLinkConnectorUtils.GetConnectorOffset(this.getStartConnector(), this.getStartConnectorType(), connectorTemplate, stroke, this);
  }
  return 0;
};

/**
 * @override
 */
dvt.AdfDiagramLink.prototype.getEndConnectorOffset = function() {

  var debug = false;
  if (debug) alert('get end connector end offset');

  if (this.getEndConnector())
  {

    var stroke;

    var connectorTemplate = this.getConnectorTemplate(dvt.DiagramLinkConnectorUtils.END_CONNECTOR);
    if (!connectorTemplate)
    {
      stroke = this.getShape().getStroke().clone();
      stroke.setType(dvt.Stroke.SOLID);
    }

    if (debug) alert('is endConn: get connector end offset');

    return dvt.DiagramLinkConnectorUtils.GetConnectorOffset(this.getEndConnector(), this.getEndConnectorType(), connectorTemplate, stroke, this);
  }
  return 0;
};

/**
 * @override
 */
dvt.AdfDiagramLink.prototype.setSelected = function(selected) {
  dvt.AdfDiagramLink.superclass.setSelected.call(this, selected);
  this._refreshSelectionEffect();
  this.UpdateAccessibilityAttributes();
  if (!this.GetDiagram().isReadOnly() && this.isUpdateEnabled()) {
    if (selected) {
      this.hideConnectors();
      this.showUpdateHandles();
    } else {
      this.hideUpdateHandles();
      this.showConnectors();
    }
  }
};

dvt.AdfDiagramLink.prototype._refreshSelectionEffect = function() {
  // We usually only update the hover effect via the selection handler, which already checks whether we're selectable.
  // _refreshSelectionEffect happens outside of this mechanism, so we need to make the check explicitly
  var hover = this.isSelectable() && this._hover;
  if (hover || this._bFocusEffect || this._selected) {
    this._showFeedback(hover || this._bFocusEffect, this._selected);
  }
  else if (this._bHighlight) {
    this._hideFeedback();
  }
};

/**
 * @override
 */
dvt.AdfDiagramLink.prototype.getCategories = function() {
  if (!this._categories) {
    this._categories = new Array();
    var parentId = this.getData().getStartId();
    var childId = this.getData().getEndId();
    var parent = this.GetDiagram().getNodeById(parentId);
    var child = this.GetDiagram().getNodeById(childId);
    var parentCategories = parent.getCategories();
    var childCategories = child.getCategories();
    if (parentCategories && childCategories) {
      for (var i = 0; i < parentCategories.length; i++) {
        if (dvt.ArrayUtils.getIndex(childCategories, parentCategories[i]) >= 0) {
          this._categories.push(parentCategories[i]);
        }
      }
    }
  }
  return this._categories;
};

dvt.AdfDiagramLink.prototype.getDisplayables = function() {
  var ar = [this];
  return ar;
};

/**
 * @protected
 * Inserts link into a parent container
 * @param {dvt.Container} parent parent container
 */
dvt.AdfDiagramLink.prototype.InsertLinkIntoParent = function(parent) {
  if (!parent)
    return;
  if (this._bCrossContainerLink) {
    parent.addChild(this);
  }
  else {
    parent.addChildAt(this, 0);
  }
};

dvt.AdfDiagramLink.prototype.GetParentContainer = function() {
  var parent = null;
  var diagram = this.GetDiagram();
  if (diagram) {
    var parent = this._linkParent;
    if (!parent) {
      parent = diagram._linksPane;
    }
  }
  return parent;
};

/**
 * Sets visibility of the link.
 * @param {boolean} bVisible True to make the link visible
 */
dvt.AdfDiagramLink.prototype.setVisible = function(bVisible) {
  //Store the visible state before changing it
  if (this._bVisible != bVisible)
    this.GetDiagram().setAutomationState(this.getData().getId(), 'visible', this._bVisible);
  dvt.AdfDiagramLink.superclass.setVisible.call(this, bVisible);

  var diagram = this.GetDiagram();
  if (diagram) {
    var parent = this.GetParentContainer();
    var contained = parent.contains(this);
    if (bVisible && !contained) {
      this.InsertLinkIntoParent(parent);
      this._cachedDiagram = null;
    }
    else if (!bVisible && contained) {
      parent.removeChild(this);
      this._cachedDiagram = diagram;
    }
  }
};


/**
 * Create label
 * @protected
 * @return {dvt.Text} label text
 */
dvt.AdfDiagramLink.prototype.CreateLabel = function() {
  var label = null;
  var labelTemplate = this.GetLabelTemplate();
  if (labelTemplate)
  {
    var afContext = this.GetDiagram().createAfContext();

    afContext.setELContext(this.getData().getElAttributes());
    label = dvt.AfComponentFactory.parseAndLayout(afContext, labelTemplate, this);
  }
  else if (this._label)
  {
    label = new dvt.OutputText(this.getCtx(), this._label, 0, 0);
    if (this._linkLabelStyles)
      label.setCSSStyle(this._linkLabelStyles);
    //this.GetDiagram().getEventManager().associate(label, this);
  }
  return label;
};

/**
 * @override
 */
dvt.AdfDiagramLink.prototype.getLabelBounds = function() {
  var bounds = null;
  if (this._labelObj)
  {
    bounds = this._labelObj.getDimensions();
  }
  return bounds;
};

/**
 * @override
 */
dvt.AdfDiagramLink.prototype.getConnectorTemplate = function(connectP) {
  var linkDef = this.getLinkDef();
  return linkDef ? linkDef.getConnectorTemplate(connectP) : null;
};

dvt.AdfDiagramLink.prototype.GetLabelTemplate = function() {
  var linkDef = this.getLinkDef();
  return linkDef ? linkDef.getLabelTemplate() : null;
};

dvt.AdfDiagramLink.prototype.HasLabel = function() {
  return (this._label || this.GetLabelTemplate());
};

/**
 * Get the Af Component in link
 * @param {String} componentId component id
 * @return {DvtAfObject}  Af Object
 */
dvt.AdfDiagramLink.prototype.getAfComponent = function(componentId) {
  var element;
  //check end connector
  var afComp = dvt.AfComponentFactory.getAfComponent(this.getEndConnector());
  if (afComp && afComp.getId() == componentId) {
    element = afComp;
  } else {
    // check start connector
    afComp = dvt.AfComponentFactory.getAfComponent(this.getStartConnector());
    if (afComp && afComp.getId() == componentId) {
      element = afComp;
    } else {
      //check link label object
      if (this.GetLabelTemplate() && this._labelObj) {
        afComp = dvt.AfComponentFactory.getAfComponent(this._labelObj);
        if (afComp && afComp.getId() == componentId)
          element = afComp;
      }
    }
  }
  return element;
};

/**
 * @override
 */
dvt.AdfDiagramLink.prototype.setPromoted = function(bPromoted) {
  if (this._bPromoted != bPromoted) {
    this._bDirty = true;
  }
  this._bPromoted = bPromoted;
};

/**
 *
 */
dvt.AdfDiagramLink.prototype.setPromotionCount = function(count) {
  this._promotionCount = count;
};


/**
 *
 */
dvt.AdfDiagramLink.prototype.getPromotionCount = function() {
  return this._promotionCount;
};


/**
 * Adds link editing handles to the link
 */
dvt.AdfDiagramLink.prototype.showUpdateHandles = function() {
  if (!this._startHandle) {
    var startHandle = new DvtAdfDiagramLinkEndpoint(this.getCtx(), this, DvtAdfDiagramLinkEndpoint.START_TYPE, this.getLinkStart(), this.getId(), this._diagram.getEditorStyles());
    this._startHandle = startHandle;
    this.addChild(startHandle);
  }
  if (!this._endHandle) {
    var endHandle = new DvtAdfDiagramLinkEndpoint(this.getCtx(), this, DvtAdfDiagramLinkEndpoint.END_TYPE, this.getLinkEnd(), this.getId(), this._diagram.getEditorStyles());
    this._endHandle = endHandle;
    this.addChild(endHandle);
  }
  this.GetDiagram().moveToDiagramFront(this);
};

dvt.AdfDiagramLink.prototype.hideUpdateHandles = function() {
  if (this._startHandle)
    this.removeChild(this._startHandle);
  if (this._endHandle)
    this.removeChild(this._endHandle);
  this._startHandle = null;
  this._endHandle = null;
  this.GetDiagram().moveToLinks(this);
};

dvt.AdfDiagramLink.prototype.hideConnectors = function() {
  if (this.getStartConnector()) {
    this.getShape().removeChild(this.getStartConnector());
    if (this._linkUnderlay)
      this._linkUnderlay.hideUnderlayStart();
  }
  if (this.getEndConnector()) {
    this.getShape().removeChild(this.getEndConnector());
    if (this._linkUnderlay)
      this._linkUnderlay.hideUnderlayEnd();
  }
};

dvt.AdfDiagramLink.prototype.showConnectors = function() {
  if (this.getStartConnector()) {
    this.getShape().addChild(this.getStartConnector());
    if (this._linkUnderlay)
      this._linkUnderlay.showUnderlayStart();
  }
  if (this.getEndConnector()) {
    this.getShape().addChild(this.getEndConnector());
    if (this._linkUnderlay)
      this._linkUnderlay.showUnderlayEnd();
  }
};

dvt.AdfDiagramLink.prototype.startLinkChange = function(type, point) {
  this._initialPoints = this.getPoints();
  this._dragItems[type] = true;
  this.setMouseEnabled(false);
};

dvt.AdfDiagramLink.prototype.updateLinkChange = function(type, point) {
  var start;
  var end;
  if (type == DvtAdfDiagramLinkEndpoint.START_TYPE) {
    start = point;
    end = this.getLinkEnd();
  } else {
    start = this.getLinkStart();
    end = point;
  }
  this.setPoints([start.x, start.y, end.x, end.y]);
  this.renderLink();
};
dvt.AdfDiagramLink.prototype.endLinkChange = function(type) {
  this._dragItems[type] = false;
};
dvt.AdfDiagramLink.prototype.endLinkChangeNewNode = function(type, centerPoint) {
  this.endLinkChange(type);
  this.updateLinkChange(type, centerPoint);
  this.setMouseEnabled(true);
};

dvt.AdfDiagramLink.prototype.endLinkChangeRestore = function(type) {
  this.endLinkChange(type);
  var stillDragging = false;
  for (var type in this._dragItems) {
    if (this._dragItems[type])
      stillDragging = true;
  }
  if (!stillDragging) {
    this.restoreLinkPosition();
    this._initialPoints = null;
  }
  this.setMouseEnabled(true);
};

dvt.AdfDiagramLink.prototype.restoreLinkPosition = function() {
  this.setPoints(this._initialPoints);
  this.renderLink();
};

/**
 * @protected
 * Get label bounds relative to this link, taking rotation and position into account.
 */
dvt.AdfDiagramLink.prototype.GetRelativeLabelBounds = function(pos, bounds, rotAngle, rotPoint) {
//Bug 31943600 :Added reference of the base class as current class does not include CalcLabelMatrix 
//Base class name is refactor from  BaseDiagramLink to dvt.BaseDiagramLink 
  var mat = dvt.BaseDiagramLink.CalcLabelMatrix(pos, bounds, rotAngle, rotPoint);
  var topLeft = mat.transformPoint(new dvt.Point(bounds.x, bounds.y));
  var topRight = mat.transformPoint(new dvt.Point(bounds.x + bounds.w, bounds.y));
  var bottomLeft = mat.transformPoint(new dvt.Point(bounds.x, bounds.y + bounds.h));
  var bottomRight = mat.transformPoint(new dvt.Point(bounds.x + bounds.w, bounds.y + bounds.h));
  var minX = Math.min(Math.min(topLeft.x, topRight.x), Math.min(bottomLeft.x, bottomRight.x));
  var maxX = Math.max(Math.max(topLeft.x, topRight.x), Math.max(bottomLeft.x, bottomRight.x));
  var minY = Math.min(Math.min(topLeft.y, topRight.y), Math.min(bottomLeft.y, bottomRight.y));
  var maxY = Math.max(Math.max(topLeft.y, topRight.y), Math.max(bottomLeft.y, bottomRight.y));
  return new dvt.Rectangle(minX, minY, maxX - minX, maxY - minY);
};

dvt.AdfDiagramLink.prototype.SetMaxAlpha = function(maxAlpha) {
  this._maxAlpha = maxAlpha;
};

dvt.AdfDiagramLink.prototype.GetMaxAlpha = function() {
  return this._maxAlpha;
};

// Implementation of DvtKeyboardNavigable interface


/**
 * @override
 */
dvt.AdfDiagramLink.prototype.getNextNavigable = function(event) 
{
  var nodes = [];
  if (event.type == dvt.MouseEvent.CLICK)
  {
    return this;
  }
  else if (event.keyCode == dvt.KeyboardEvent.SPACE && event.ctrlKey)
  {
    // multi-select node with current focus; so we navigate to ourself and then let the selection handler take
    // care of the selection
    return this;
  }
  else if (event.keyCode == dvt.KeyboardEvent.UP_ARROW || event.keyCode == dvt.KeyboardEvent.DOWN_ARROW)
  {
    //if the link got focus via keyboard, get the node where the focus came from
    //we'll navigate around that node
    //if the focus was set through mouse click, set start node as a center of navigation
    this._updateKeyboardFocusEffect(null);
    var node = this.getKeyboardFocusNode();
    if (!node)
      node = this._diagram.getNodeById(this._diagram.getLinkStartId(this));

    //find next link - if up counter-clockwise, down - clockwise
    var nextLink = this;
    var links = this._diagram.getNavigableLinksForNodeId(node.getData().getId());
    var keyboardHandler = this._diagram.getEventManager().getKeyboardHandler();
    if (keyboardHandler && keyboardHandler.getNextNavigableLink)
      nextLink = keyboardHandler.getNextNavigableLink(node, this, event, links);

    nextLink.setKeyboardFocusNode(node);
    return nextLink;
  }
  else if (event.keyCode == dvt.KeyboardEvent.RIGHT_ARROW || event.keyCode == dvt.KeyboardEvent.LEFT_ARROW)
  {
    // select a connector or jump to the adjacent node
    var connector = this._findConnectorByDirection(event.keyCode);
    if (connector) {
      this._updateKeyboardFocusEffect(connector);
      this._preserveKeyboardFocusEffect = true; //to preserve the connector focus - event manager is going to reset it
      return this;
    }
    else {
      this._updateKeyboardFocusEffect(null);
      var nodeId;
      if (this._movingToStart(event.keyCode))
        nodeId = this._diagram.getLinkStartId(this);
      else
        nodeId = this._diagram.getLinkEndId(this);
      return this._diagram.getNodeById(nodeId);
    }
  }

  //TO DO: implement correctly (this was just copied from dvt.AdfDiagramNode)
  var nodesByGroupDepth = this._diagram.getSortedVisibleNodesByGroupDepth();
  nodes = nodesByGroupDepth[0];

  // TODO: choose correct depth
  return dvt.KeyboardHandler.getNextAdjacentNavigable(this, event, nodes);
};

/**
 * @override
 */
dvt.AdfDiagramLink.prototype.showKeyboardFocusEffect = function() 
{
  this.showHoverEffect();
  this._bFocusEffect = true;
  this.setFocused(true);

};


/**
 * @override
 */
dvt.AdfDiagramLink.prototype.hideKeyboardFocusEffect = function() 
{
  this._bFocusEffect = false;
  if (!this._hover) {
    this.hideHoverEffect();
  }
  this.setFocused(false);
};


/**
 * @override
 */
dvt.AdfDiagramLink.prototype.setFocused = function(bFocused) {
  this._bFocused = bFocused;
};


/**
 * @override
 */
dvt.AdfDiagramLink.prototype.isFocused = function() {
  return this._bFocused;
};


/**
 * @override
 */
dvt.AdfDiagramLink.prototype.isShowingKeyboardFocusEffect = function() 
{
  return this._bFocusEffect;
};

/**
 * store context information in a dvt.BaseComponentEvent event.
 * @param {dvt.BaseComponentEvent} event
 *
 */
dvt.AdfDiagramLink.prototype.eventContextCallback = function(event) {
  // store clientRowKey in this event
  if (event) {
    event[dvt.BaseComponentEvent.CLIENT_ROW_KEY] = this.getData().getId();
  }

  this.GetDiagram().HideNodeTransientToolbar();
};


/**
 * Get show popup behaviors of diagram link
 * @return {array}  Array of ShowPopupBehavior
 */
dvt.AdfDiagramLink.prototype.getShowPopupBehaviors = function() {
  if (!this._showPopupBehaviors && this._linkDef && this._linkDef.getShowPopupBehaviors()) {
    var afShowPopupBehaviors = this._linkDef.getShowPopupBehaviors();
    var afContext = this.GetDiagram().createAfContext();

    afContext.setELContext(this.getData().getElAttributes());
    this._showPopupBehaviors = [];
    for (var i = 0; i < afShowPopupBehaviors.length; i++) {
      this._showPopupBehaviors[i] = dvt.AfComponentFactory.parseAndStamp(afContext, afShowPopupBehaviors[i], null);
    }
  }
  return this._showPopupBehaviors;
};

/**
 * @override
 */
dvt.AdfDiagramLink.prototype.getAriaLabel = function() {
  return dvt.Displayable.generateAriaLabel(this.getShortDesc(), this.getAccessibilityStates());
};


/**
 * Gets states for the link that are going to be used for WAI-ARIA label
 * @return {array} array of states for the link
 */
dvt.AdfDiagramLink.prototype.getAccessibilityStates = function() {
  var states = [];
  if (this.isSelectable()) {
    states.push(this.isSelected() ? dvt.Bundle.getTranslatedString(dvt.Bundle.UTIL_PREFIX, 'STATE_SELECTED') :
        dvt.Bundle.getTranslatedString(dvt.Bundle.UTIL_PREFIX, 'STATE_UNSELECTED'));
  }
  if (this.isPromoted()) {
    states.push(dvt.Bundle.getTranslatedString(dvt.Bundle.ADF_DIAGRAM_PREFIX, 'PROMOTED_LINK_ARIA_DESC'));
  }
  return states;
};


/**
 * Updates WAI-ARIA attributes for the link
 * @protected
 */
dvt.AdfDiagramLink.prototype.UpdateAccessibilityAttributes = function() {
  if (!dvt.Agent.deferAriaCreation()) {
    var desc = this.getAriaLabel();
    if (desc) {
      this.setAriaProperty('label', desc);
    }
  }
};


/**
 * Adds WAI-ARIA attributes to the node
 */
dvt.AdfDiagramLink.prototype.addAccessibilityAttributes = function() {
  // WAI-ARIA
  var desc = this.getAriaLabel();
  if (desc) {
    this.setAriaRole('img');
    this.setAriaProperty('label', desc);
  }
};

/**
 * @protected
 * Copy the state to this diagram link from the passed link
 * @param {dvt.AdfDiagramLink} fromLink  Diagram link to copy from
 */
dvt.AdfDiagramLink.prototype.CopyState = function(fromLink) {
  this._categories = dvt.ArrayUtils.copy(fromLink._categories);

  this._selected = fromLink._selected;
  this._hover = fromLink._hover;

  this._bFocused = fromLink._bFocused;
  this._bFocusEffect = fromLink._bFocusEffect;
  this._bVisible = fromLink._bVisible;

  this._arPoints = dvt.ArrayUtils.copy(fromLink._arPoints);
  this._points = dvt.ArrayUtils.copy(fromLink._points);
  this._pathCmds = dvt.ArrayUtils.copy(fromLink._pathCmds);
};

dvt.AdfDiagramLink.prototype.destroy = function() {
  this.DestroyContent();

  dvt.AdfDiagramLink.superclass.destroy.call(this);
};

dvt.AdfDiagramLink.prototype.CreateCustomAnimPlayable = function() {
  return new dvt.CustomAnimation(this.getCtx(), null, this.GetDiagram().getAnimationDuration());
};


/**
 * Shows animated transformation of the link from old state to new
 * @param {DvtAdfDiagramDataAnimationHandler} dataAnimHandler Animation handler
 * @param {dvt.AdfDiagramLink} oldLink Old link
 */
dvt.AdfDiagramLink.prototype.AnimateUpdate = function(dataAnimHandler, oldLink) {
  var playable = null;

  //if visibility is changing, treat the update like an insert or delete,
  //and don't have to consider any other changes to the link
  var oldVisible = oldLink.getVisible();
  var oldPoints = oldLink.getPoints();
  var newVisible = this.getVisible();
  if (oldVisible != newVisible || !oldPoints) {
    if (!playable) {
      playable = this.CreateCustomAnimPlayable();
    }
    if (oldVisible && oldPoints) {
      //treat like a delete
      var linkParent = this.GetParentContainer();
      oldLink.InsertLinkIntoParent(linkParent);
      playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, oldLink, oldLink.getAlpha, oldLink.setAlpha, 0);
      dvt.Playable.appendOnEnd(playable, function() {
        oldLink.getParent().removeChild(oldLink);
        oldLink.destroy();
      });
      dataAnimHandler.add(playable, DvtAdfDiagramDataAnimationHandler.PHASE_DELETE);
    }
    else if (newVisible) {
      //treat like an insert
      this.setAlpha(0);
      playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, this, this.getAlpha, this.setAlpha, this.GetMaxAlpha());
      dataAnimHandler.add(playable, DvtAdfDiagramDataAnimationHandler.PHASE_INSERT);
    }

    //the link is either appearing or disappearing, so no need to
    //worry about other changes
    //playable = null;
    return;
  }

  var oldPoints = oldLink.getPoints();
  var newPoints = this.getPoints();
  if (!dvt.ArrayUtils.equals(oldPoints, newPoints)) {
    if (!playable) {
      playable = this.CreateCustomAnimPlayable();
    }
    this.setPoints(oldPoints);
    var linkAnimType = dvt.Animator.TYPE_POLYLINE;
    if (newPoints.length > 0 && isNaN(newPoints[0])) {
      linkAnimType = dvt.Animator.TYPE_PATH;
    }
    playable.getAnimator().addProp(linkAnimType, this, this.getPoints, this.setPoints, newPoints);
  }

  if (oldLink._labelObj && this._labelObj) {
    var oldMat = oldLink._labelObj.getMatrix();
    var newMat = this._labelObj.getMatrix();
    if (!oldMat.equals(newMat)) {
      if (!playable) {
        playable = this.CreateCustomAnimPlayable();
      }
      this._labelObj.setMatrix(oldMat);
      playable.getAnimator().addProp(dvt.Animator.TYPE_MATRIX, this._labelObj, this._labelObj.getMatrix, this._labelObj.setMatrix, newMat);
    }
  }

  if (oldLink.getShape() && this.getShape()) {
    var oldStroke = oldLink.getShape().getStroke();
    var newStroke = this.getShape().getStroke();
    if (oldStroke && newStroke &&
        oldStroke instanceof dvt.SolidStroke &&
        newStroke instanceof dvt.SolidStroke &&
        (oldStroke.getColor() != newStroke.getColor() ||
         oldStroke.getWidth() != newStroke.getWidth())) {
      if (!playable) {
        playable = this.CreateCustomAnimPlayable();
      }
      this.getShape().setStroke(oldStroke);
      playable.getAnimator().addProp(dvt.Animator.TYPE_STROKE, this.getShape(), this.getShape().getStroke, this.getShape().setStroke, newStroke);
    }
  }

  if (playable) {
    dataAnimHandler.add(playable, DvtAdfDiagramDataAnimationHandler.PHASE_UPDATE);
  }
};

/**
 * @protected
 * Animates insertion of a diagram node
 * @param {DvtAdfDiagramDataAminationHandler} dataAnimHandler daigram data animation handler
 */
dvt.AdfDiagramLink.prototype.AnimateInsert = function(dataAnimHandler) {
  var bAnim = true;
  var groupId = this.getData().getGroupId();
  if (groupId) {
    var groupNode = this.GetDiagram()._oldNodes[groupId];
    //if the group didn't exist in the old diagram, then this node
    //is a descendant of a group being inserted, so no need to
    //animate it separately because the top level inserted group
    //will handle it
    if (!groupNode) {
      bAnim = false;
    }
  }
  if (bAnim) {
    var playable = new dvt.CustomAnimation(this.getCtx(), this.GetDiagram().getAnimationDuration());
    var anim = playable.getAnimator();
    this.setAlpha(0);
    anim.addProp(dvt.Animator.TYPE_NUMBER, this, this.getAlpha, this.setAlpha, this._maxAlpha);
    dataAnimHandler.add(playable, DvtAdfDiagramDataAnimationHandler.PHASE_INSERT);
  }
};

/**
 * @protected
 * Animates deletion of a diagram link
 * @param {DvtAdfDiagramDataAminationHandler} dataAnimHandler daigram data animation handler
 * @param {dvt.AdfDiagram} newDiagram new diagram
 */
dvt.AdfDiagramLink.prototype.AnimateDelete = function(dataAnimHandler, newDiagram) {
  var bAnim = true;
  var groupId = this.getData().getGroupId();
  if (groupId) {
    var groupNode = newDiagram.getNodeById(groupId);
    //if the group node exists in the new diagram, then reparent
    //this link to it and animate it
    if (groupNode && groupNode.GetChildNodePane()) {
      this.InsertLinkIntoParent(groupNode.GetChildNodePane());
    }
    //if the group node doesn't exist in the new diagram, then
    //this link must be a descendant of a group being deleted, so
    //don't need to reparent or animate this link separately because
    //the top level group being deleted will take care of it
    else {
      bAnim = false;
    }
  }
  else {
    //if not in a group, then reparent this link and animate it
    this.InsertLinkIntoParent(newDiagram._linksPane);
  }
  if (bAnim) {
    var playable = new dvt.CustomAnimation(this.getCtx(), newDiagram.getAnimationDuration());
    var anim = playable.getAnimator();
    anim.addProp(dvt.Animator.TYPE_NUMBER, this, this.getAlpha, this.setAlpha, 0);
    var thisRef = this;
    var removeFunc = function() {
      thisRef.getParent().removeChild(thisRef);
      thisRef.destroy();
    };
    dvt.Playable.appendOnEnd(playable, removeFunc);
    dataAnimHandler.add(playable, DvtAdfDiagramDataAnimationHandler.PHASE_DELETE);
  }
};

dvt.AdfDiagramLink.prototype.setKeyboardFocusConnector = function(connector) {
  this._keyboardNavConnector = connector;
};

dvt.AdfDiagramLink.prototype.getKeyboardFocusConnector = function() {
  return this._keyboardNavConnector;
};

dvt.AdfDiagramLink.prototype._updateKeyboardFocusEffect = function(focusable) {

  var prevFocus = this.getKeyboardFocusConnector();
  if (prevFocus && prevFocus.hideKeyboardFocusEffect)
    prevFocus.hideKeyboardFocusEffect();
  if (focusable && focusable.showKeyboardFocusEffect)
    focusable.showKeyboardFocusEffect();
  this.setKeyboardFocusConnector(focusable);
};

dvt.AdfDiagramLink.prototype._movingToStart = function(direction) {
  var start = this.getLinkStart();
  var end = this.getLinkEnd();
  var linkDirectionL2R = (start.x < end.x) ? true : false;

  if ((direction == dvt.KeyboardEvent.RIGHT_ARROW && linkDirectionL2R) ||
      (direction == dvt.KeyboardEvent.LEFT_ARROW && !linkDirectionL2R))
    return false;
  else
    return true;
};

dvt.AdfDiagramLink.prototype._findConnectorByDirection = function(direction) {

  var connector = null;
  if (this._movingToStart(direction))
    connector = this.getStartConnector();
  else
    connector = this.getEndConnector();

  //we have a connector, but is it a focusable connector ?
  var focusable = this.findFocusableObject(connector);

  // if this is the same connector, we should not select it
  // but move focus to the node instead
  if (this.getKeyboardFocusConnector() == focusable)
    focusable = null;
  return focusable;
};

/**
 * Keyboard event handler
 * @param {dvt.KeyboardEvent} event keyboard event
 * @protected
 */
dvt.AdfDiagramLink.prototype.HandleKeyboardEvent = function(event) {
  var keyCode = event.keyCode;

  if (keyCode == dvt.KeyboardEvent.ENTER) {
    var eventManager = this.GetDiagram().getEventManager();
    var target = this.getShape();
    var point = target.localToStage(this.CalPointForMouseEvent());
    var mouseEvent = dvt.DomEventFactory.generateMouseEventFromKeyboardEvent(event, this.getCtx(), dvt.MouseEvent.CLICK,
        this.getCtx().getStage(),
        point.x, point.y);
    mouseEvent.target = target;
    eventManager.PreOnClick(mouseEvent);
  }
};

dvt.AdfDiagramLink.prototype.CalPointForMouseEvent = function() {
  var points = this._points;
  var x = 0, y = 0;
  if (points.length > 4) {
    var mid = points.length / 2;
    if (mid % 2 == 0) {
      x = points[mid];
      y = points[mid + 1];
    }
    else {
      x = points[mid - 1];
      y = points[mid];
    }
  }
  else if (points.length == 4) {
    x = (points[0] + points[2]) / 2;
    y = (points[1] + points[3]) / 2;
  }
  return new dvt.Point(x, y);
};

dvt.AdfDiagramLink.prototype.IsStandardConnector = function(position) {
  var connectorType = (position == 0) ? this.getStartConnectorType() : this.getEndConnectorType();
  if (connectorType)
    return true;
  else
    return false;
};

dvt.AdfDiagramLink.prototype.findFocusableObject = function(connector) {

  for (var i = 0; i < this._tabStopsConnectorArray.length; i++) {
    var focusable = this._tabStopsConnectorArray[i];
    if ((focusable instanceof dvt.AfComponent) && (focusable.getDisplayObj() == connector))
      return focusable;
  }
  return null;
};

dvt.AdfDiagramLink.prototype.getTabStopsArray = function() {
  if (!this._tabStopsConnectorArray)
    this._tabStopsConnectorArray = [];
  return this._tabStopsConnectorArray;
};

dvt.AdfDiagramLink.prototype.getCurrentTabStop = function() {
  return this.getKeyboardFocusConnector();
};

dvt.AdfDiagramLink.prototype.resetTabStops = function() {
  this.setKeyboardFocusConnector(null);
};


/**
 * Sets the cursor for this link
 */
dvt.AdfDiagramLink.prototype._refreshCursor = function() {
  var selectingCursor = dvt.SelectionEffectUtils.getSelectingCursor();
  if (this.isSelectable()) {
    this.setCursor(selectingCursor);
    return;
  }
  var spbs = this.getShowPopupBehaviors();
  if (dvt.ArrayUtils.isArray(spbs)) {
    for (var i = 0; i < spbs.length; i++) {
      if (spbs[i] && spbs[i].getTriggerType() == 'click') {
        this.setCursor(selectingCursor);
        return;
      }
    }
  }
  this.setCursor(null);
};

/**
 * Get the automation state of the link for the passed state key
 * This will return pre-transition state value until the link animation is complete.
 * @param {string} state   State key
 * @return {boolean}  state value of the diagram link
 */
dvt.AdfDiagramLink.prototype.getAutomationState = function(state) {
  var value = this.GetDiagram().getAutomationState(this.getData().getId(), state);
  if (value == null) {
    if (state == 'visible')
      return this.getVisible();
  }
  return value;
};

/**
 * @override
 */
dvt.AdfDiagramLink.prototype.getGroupId = function() {
  return this.getData().getGroupId();
};
/* Copyright (c) 2011, 2016, Oracle and/or its affiliates. 
All rights reserved.*/

/**
 * @constructor
 */
dvt.AdfDiagramLinkDef = function()
{
  this.Init();
};

dvt.Obj.createSubclass(dvt.AdfDiagramLinkDef, dvt.Obj, 'dvt.AdfDiagramLinkDef');

dvt.AdfDiagramLinkDef.LINK_TYPE = '__linkType__';                 // link type custom metadata (for HV layouts)
dvt.AdfDiagramLinkDef.LINK_TYPE_DIRECT = 'direct';                 // straight line
dvt.AdfDiagramLinkDef.LINK_TYPE_DIRECT_ROUNDED = 'directRounded';  // line broken into single right angle with rounded corner
dvt.AdfDiagramLinkDef.LINK_TYPE_DIRECT_SMOOTH = 'directSmooth';    // smooth curve fitted to what would have been single right angle
dvt.AdfDiagramLinkDef.LINK_TYPE_ORTHOGONAL = 'orthogonal';         // line broken into horizontal and vertical segments with square corners
dvt.AdfDiagramLinkDef.LINK_TYPE_ORTHOGONAL_ROUNDED = 'orthogonalRounded';  // line broken into horizontal and vertical segments with rounded corners
dvt.AdfDiagramLinkDef.LINK_TYPE_ORTHOGONAL_SMOOTH = 'orthogonalSmooth';  // smooth curve fitted to what would have been orthogonal line segments
dvt.AdfDiagramLinkDef.LINK_TYPE_CURVED = 'curved';

dvt.AdfDiagramLinkDef.LINK_STYLE_SOLID = 'solid';
dvt.AdfDiagramLinkDef.LINK_STYLE_DASH = 'dash';
dvt.AdfDiagramLinkDef.LINK_STYLE_DOT = 'dot';
dvt.AdfDiagramLinkDef.LINK_STYLE_DASH_DOT = 'dashDot';

dvt.AdfDiagramLinkDef.prototype.Init = function()
{
  this._id = null;

  this._propMap = null;
  this._connectorTemplates;
  this._labelTemplate;

  this._showPopupBehaviors = null;
};

dvt.AdfDiagramLinkDef.prototype.setId = function(id)
{
  this._id = id;
};

dvt.AdfDiagramLinkDef.prototype.getId = function()
{
  return this._id;
};


/**
 * Sets links width
 * @param {number} lw link width
 */
dvt.AdfDiagramLinkDef.prototype.setLinkWidth = function(lw)
{
  if (!this._propMap)
  {
    this._propMap = new dvt.PropMap();
  }
  this._propMap.setProperty(dvt.AdfDiagramParser._ATTR_LINK_WIDTH, lw);
};


/**
 * Gets link width
 * @return {number} link width
 */
dvt.AdfDiagramLinkDef.prototype.getLinkWidth = function()
{
  if (this._propMap)
  {
    return this._propMap.getProperty(dvt.AdfDiagramParser._ATTR_LINK_WIDTH);
  }

  return null;
};


/**
 * Sets links length
 * @param {number} ll link length
 */
dvt.AdfDiagramLinkDef.prototype.setLinkLength = function(ll)
{
  if (!this._propMap)
  {
    this._propMap = new dvt.PropMap();
  }
  this._propMap.setProperty(dvt.AdfDiagramParser._ATTR_LINK_LENGTH, ll);
};


/**
 * Gets link length
 * @return {number} link length
 */
dvt.AdfDiagramLinkDef.prototype.getLinkLength = function()
{
  if (this._propMap)
  {
    return this._propMap.getProperty(dvt.AdfDiagramParser._ATTR_LINK_LENGTH);
  }

  return null;
};

dvt.AdfDiagramLinkDef.prototype.setLinkColor = function(lc)
{
  if (!this._propMap)
  {
    this._propMap = new dvt.PropMap();
  }
  this._propMap.setProperty(dvt.AdfDiagramParser._ATTR_LINK_COLOR, lc);
};

dvt.AdfDiagramLinkDef.prototype.getLinkColor = function()
{
  if (this._propMap)
  {
    return this._propMap.getProperty(dvt.AdfDiagramParser._ATTR_LINK_COLOR);
  }

  return null;
};

dvt.AdfDiagramLinkDef.prototype.setLinkType = function(lt)
{
  if (!this._propMap)
  {
    this._propMap = new dvt.PropMap();
  }
  this._propMap.setProperty(dvt.AdfDiagramParser._ATTR_LINK_TYPE, lt);
};

dvt.AdfDiagramLinkDef.prototype.getLinkType = function()
{
  if (this._propMap)
  {
    return this._propMap.getProperty(dvt.AdfDiagramParser._ATTR_LINK_TYPE);
  }

  return null;
};

dvt.AdfDiagramLinkDef.prototype.setLinkStyle = function(ls)
{
  if (!this._propMap)
  {
    this._propMap = new dvt.PropMap();
  }
  this._propMap.setProperty(dvt.AdfDiagramParser._ATTR_LINK_STYLE, ls);
};

dvt.AdfDiagramLinkDef.prototype.getLinkStyle = function()
{
  if (this._propMap)
  {
    return this._propMap.getProperty(dvt.AdfDiagramParser._ATTR_LINK_STYLE);
  }

  return null;
};

dvt.AdfDiagramLinkDef.prototype.setLabel = function(label)
{
  if (!this._propMap)
  {
    this._propMap = new dvt.PropMap();
  }
  this._propMap.setProperty(dvt.AdfDiagramParser._ATTR_LABEL, label);
};

dvt.AdfDiagramLinkDef.prototype.getLabel = function()
{
  if (this._propMap)
  {
    return this._propMap.getProperty(dvt.AdfDiagramParser._ATTR_LABEL);
  }

  return null;
};

dvt.AdfDiagramLinkDef.prototype.setLabelTemplate = function(template)
{
  this._labelTemplate = template;
};

dvt.AdfDiagramLinkDef.prototype.getLabelTemplate = function()
{
  return this._labelTemplate;
};

dvt.AdfDiagramLinkDef.prototype.setRendered = function(rendered)
{
  if (!this._propMap)
  {
    this._propMap = new dvt.PropMap();
  }
  this._propMap.setProperty(dvt.AdfDiagramParser._ATTR_AF_RENDERED, rendered);
};

dvt.AdfDiagramLinkDef.prototype.getRendered = function()
{
  if (this._propMap)
  {
    return this._propMap.getProperty(dvt.AdfDiagramParser._ATTR_AF_RENDERED);
  }

  return true;
};

dvt.AdfDiagramLinkDef.prototype.setReadOnly = function(readOnly)
{
  if (!this._propMap)
  {
    this._propMap = new dvt.PropMap();
  }
  this._propMap.setProperty(dvt.AdfDiagramParser._ATTR_AF_READ_ONLY, readOnly);
};

dvt.AdfDiagramLinkDef.prototype.getReadOnly = function()
{
  if (this._propMap)
  {
    return this._propMap.getProperty(dvt.AdfDiagramParser._ATTR_AF_READ_ONLY);
  }

  return false;
};

dvt.AdfDiagramLinkDef.prototype.setSelectable = function(selectable)
{
  if (!this._propMap)
  {
    this._propMap = new dvt.PropMap();
  }
  this._propMap.setProperty(dvt.AdfDiagramParser._ATTR_SELECTABLE, selectable);
};

dvt.AdfDiagramLinkDef.prototype.getSelectable = function()
{
  if (this._propMap)
  {
    return this._propMap.getProperty(dvt.AdfDiagramParser._ATTR_SELECTABLE);
  }

  return true;
};

dvt.AdfDiagramLinkDef.prototype.setShortDesc = function(shortDesc)
{
  if (!this._propMap)
  {
    this._propMap = new dvt.PropMap();
  }
  this._propMap.setProperty(dvt.AdfDiagramParser._ATTR_AF_SHORT_DESC, shortDesc);
};

dvt.AdfDiagramLinkDef.prototype.getShortDesc = function()
{
  if (this._propMap)
  {
    return this._propMap.getProperty(dvt.AdfDiagramParser._ATTR_AF_SHORT_DESC);
  }

  return null;
};

dvt.AdfDiagramLinkDef.prototype.setEndConnectorType = function(type)
{
  if (!this._propMap)
  {
    this._propMap = new dvt.PropMap();
  }
  this._propMap.setProperty(dvt.AdfDiagramParser._ATTR_END_CONNECTOR_TYPE, type);
};

dvt.AdfDiagramLinkDef.prototype.getEndConnectorType = function()
{
  if (this._propMap)
  {
    return this._propMap.getProperty(dvt.AdfDiagramParser._ATTR_END_CONNECTOR_TYPE);
  }

  return null;
};

dvt.AdfDiagramLinkDef.prototype.setStartConnectorType = function(type)
{
  if (!this._propMap)
  {
    this._propMap = new dvt.PropMap();
  }
  this._propMap.setProperty(dvt.AdfDiagramParser._ATTR_START_CONNECTOR_TYPE, type);
};

dvt.AdfDiagramLinkDef.prototype.getStartConnectorType = function()
{
  if (this._propMap)
  {
    return this._propMap.getProperty(dvt.AdfDiagramParser._ATTR_START_CONNECTOR_TYPE);
  }

  return null;
};

dvt.AdfDiagramLinkDef.prototype.setConnectorTemplate = function(template, name)
{
  if (!this._connectorTemplates)
  {
    this._connectorTemplates = {};
  }
  this._connectorTemplates[name] = template;
};

dvt.AdfDiagramLinkDef.prototype.getConnectorTemplate = function(name)
{
  if (name && this._connectorTemplates)
  {
    return this._connectorTemplates[name];
  }
  return null;
};

dvt.AdfDiagramLinkDef.prototype.addShowPopupBehavior = function(spb) {
  if (!this._showPopupBehaviors) {
    this._showPopupBehaviors = [];
  }
  this._showPopupBehaviors.push(spb);
};

dvt.AdfDiagramLinkDef.prototype.getShowPopupBehaviors = function() {
  return this._showPopupBehaviors;
};

dvt.AdfDiagramLinkDef.prototype.stamp = function(elContext, link)
{
  if (this._propMap)
  {
    var resolvedMap = this._propMap.stamp(elContext);
    link.setLinkColor(resolvedMap.getProperty(dvt.AdfDiagramParser._ATTR_LINK_COLOR));
    link.setLinkWidth(resolvedMap.getIntegerProp(dvt.AdfDiagramParser._ATTR_LINK_WIDTH));
    link.setLinkLength(resolvedMap.getIntegerProp(dvt.AdfDiagramParser._ATTR_LINK_LENGTH));
    link.setLinkType(resolvedMap.getProperty(dvt.AdfDiagramParser._ATTR_LINK_TYPE));
    link.setLinkStyle(resolvedMap.getProperty(dvt.AdfDiagramParser._ATTR_LINK_STYLE));
    link.setShortDesc(resolvedMap.getProperty(dvt.AdfDiagramParser._ATTR_AF_SHORT_DESC));
    link.setEndConnectorType(resolvedMap.getProperty(dvt.AdfDiagramParser._ATTR_END_CONNECTOR_TYPE));
    link.setStartConnectorType(resolvedMap.getProperty(dvt.AdfDiagramParser._ATTR_START_CONNECTOR_TYPE));
    link.setLabel(resolvedMap.getProperty(dvt.AdfDiagramParser._ATTR_LABEL));
    link.setRendered(resolvedMap.getBooleanProp(dvt.AdfDiagramParser._ATTR_AF_RENDERED));
    var readOnly = resolvedMap.getBooleanProp(dvt.AdfDiagramParser._ATTR_AF_READ_ONLY, false);
    link.setUpdateEnabled(!readOnly);
    link.setDeleteEnabled(!readOnly);
    link.setSelectable(resolvedMap.getBooleanProp(dvt.AdfDiagramParser._ATTR_SELECTABLE, true));
  }
};

/**
 * @constructor
 */
dvt.AdfDiagramNodeData = function()
{
  this.Init();
};

dvt.Obj.createSubclass(dvt.AdfDiagramNodeData, dvt.Obj, 'dvt.AdfDiagramNodeData');

dvt.AdfDiagramNodeData.prototype.Init = function()
{
  this._id = null;
  this._templateId = null;
  this._elAttributes = [];
  this._groupId = null;
  this._attrGrps = null;
  this._hasAnchorListener = false;
  this._layoutAttributes = null;
  //Bug 13745138: pay attention to showIsolate attribute
  this._showIsolate = true;
  this._showExpandChildren = true;
  this._showDrill = true;
  this._showStack = true;
  this._showNodeActions = true;
  this._rowIndex = -1;
  this._structureOnly = false;
  this._container = false;
  this._childrenFetched = false;
};

dvt.AdfDiagramNodeData.prototype.mergeProps = function(obj) {
  dvt.AdfDiagramNodeData.superclass.mergeProps.call(this, obj);

  obj._id = this._id;
  obj._templateId = this._templateId;
  obj._elAttributes = this._elAttributes;
  obj._groupId = this._groupId;
  obj._attrGrps = this._attrGrps;
  obj._hasAnchorListener = this._hasAnchorListener;
  obj._layoutAttributes = this._layoutAttributes;
  obj._showIsolate = this._showIsolate;
  obj._showExpandChildren = this._showExpandChildren;
  obj._showDrill = this._showDrill;
  obj._showStack = this._showStack;
  obj._showNodeActions = this._showNodeActions;
  obj._rowIndex = this._rowIndex;
  obj._structureOnly = this._structureOnly;
  obj._container = this._container;
  obj._childrenFetched = this._childrenFetched;
};

dvt.AdfDiagramNodeData.prototype.clone = function() {
  var o = new dvt.AdfDiagramNodeData();
  this.mergeProps(o);
  return o;
};

dvt.AdfDiagramNodeData.prototype.setId = function(id)
{
  this._id = id;
};

dvt.AdfDiagramNodeData.prototype.getId = function()
{
  return this._id;
};

dvt.AdfDiagramNodeData.prototype.setTemplateId = function(id)
{
  this._templateId = id;
};

dvt.AdfDiagramNodeData.prototype.getTemplateId = function()
{
  return this._templateId;
};

dvt.AdfDiagramNodeData.prototype.setElAttributes = function(attrs)
{
  this._elAttributes = attrs;
};

dvt.AdfDiagramNodeData.prototype.getElAttributes = function()
{
  return this._elAttributes;
};

//adding panelCard data
dvt.AdfDiagramNodeData.prototype.addElAttributes = function(arAttrs)
{
  var attr;
  for (var i = 0; i < arAttrs.length; i++) {
    attr = arAttrs[i];
    this._elAttributes[attr.name] = attr.value;
  }
};


/**
 * adding panelCard EL Json attributes
 * @param {Array} arAttrs Array of EL Attributes
 */
dvt.AdfDiagramNodeData.prototype.addElAttributesJson = function(arAttrs)
{
  for (var key in arAttrs) {
    this._elAttributes[key] = arAttrs[key];
  }
};

dvt.AdfDiagramNodeData.prototype.setGroupId = function(id)
{
  this._groupId = id;
};

dvt.AdfDiagramNodeData.prototype.getGroupId = function()
{
  return this._groupId;
};

dvt.AdfDiagramNodeData.prototype.setAttributeGroups = function(attrGrps)
{
  this._attrGrps = attrGrps;
};

dvt.AdfDiagramNodeData.prototype.getAttributeGroups = function()
{
  return this._attrGrps;
};

dvt.AdfDiagramNodeData.prototype.setHasAnchorListener = function(hasAnchorListener)
{
  this._hasAnchorListener = hasAnchorListener;
};

dvt.AdfDiagramNodeData.prototype.getHasAnchorListener = function()
{
  return this._hasAnchorListener;
};

dvt.AdfDiagramNodeData.prototype.setLayoutAttributes = function(layoutAttributes) {
  this._layoutAttributes = layoutAttributes;
};

dvt.AdfDiagramNodeData.prototype.getLayoutAttributes = function() {
  return this._layoutAttributes;
};

dvt.AdfDiagramNodeData.prototype.setShowIsolate = function(showIsolate)
{
  this._showIsolate = showIsolate;
};

dvt.AdfDiagramNodeData.prototype.isShowIsolate = function()
{
  return this._showIsolate;
};

dvt.AdfDiagramNodeData.prototype.setShowExpandChildren = function(showExpandChildren)
{
  this._showExpandChildren = showExpandChildren;
};

/**
 * Check whether the expand children can be displayed
 * @return {boolean} Whether the expand children can be displayed
 */
dvt.AdfDiagramNodeData.prototype.isShowExpandChildren = function()
{
  return this._showExpandChildren;
};

dvt.AdfDiagramNodeData.prototype.setShowDrill = function(showDrill)
{
  this._showDrill = showDrill;
};

dvt.AdfDiagramNodeData.prototype.isShowDrill = function()
{
  return this._showDrill;
};

dvt.AdfDiagramNodeData.prototype.setShowStack = function(showStack)
{
  this._showStack = showStack;
};

dvt.AdfDiagramNodeData.prototype.isShowStack = function()
{
  return this._showStack;
};

dvt.AdfDiagramNodeData.prototype.setShowNodeActions = function(showNodeActions)
{
  this._showNodeActions = showNodeActions;
};

dvt.AdfDiagramNodeData.prototype.isShowNodeActions = function()
{
  return this._showNodeActions;
};

dvt.AdfDiagramNodeData.prototype.setRowIndex = function(rowIndex) {
  this._rowIndex = rowIndex;
};

dvt.AdfDiagramNodeData.prototype.getRowIndex = function() {
  return this._rowIndex;
};

dvt.AdfDiagramNodeData.prototype.setStructureOnly = function(structureOnly) {
  this._structureOnly = structureOnly;
};

dvt.AdfDiagramNodeData.prototype.isStructureOnly = function() {
  return this._structureOnly;
};

dvt.AdfDiagramNodeData.prototype.setContainer = function(bContainer) {
  this._container = bContainer;
};

dvt.AdfDiagramNodeData.prototype.isContainer = function() {
  return this._container;
};

dvt.AdfDiagramNodeData.prototype.setChildrenFetched = function(childrenFetched) {
  this._childrenFetched = childrenFetched;
};

dvt.AdfDiagramNodeData.prototype.isChildrenFetched = function() {
  return this._childrenFetched;
};
// Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
/**
 * @constructor
 * @implements {DvtKeyboardNavigable}
 */
dvt.AdfDiagramNode = function(context, nodeData, diagram)
{
  this.Init(context, nodeData, diagram);
};

dvt.Obj.createSubclass(dvt.AdfDiagramNode, dvt.BaseDiagramNode, 'dvt.AdfDiagramNode');

dvt.AdfDiagramNode._CONTAINER_PADDING = 10;
dvt.AdfDiagramNode._TRANSIENT_TOOLBAR_BUTTONS_PER_ROW = 4;
dvt.AdfDiagramNode._TRANSIENT_TOOLBAR_ALPHA = .95;

dvt.AdfDiagramNode._STACK_UNDERLAY_OFFSET = 3;
dvt.AdfDiagramNode._STACK_UNDERLAY_PADDING = 10;
dvt.AdfDiagramNode._STACK_BADGE_OFFSET = 4;

dvt.AdfDiagramNode._TOOLBAR_SHADOW_STYLE = '2 2 3 0 rgba(0, 0, 0, 0.5)';
dvt.AdfDiagramNode._STACK_PREVIEW_COLOR = '#ffffff';
dvt.AdfDiagramNode._STACK_PREVIEW_ALPHA = .9;

dvt.AdfDiagramNode._SELECTION_FILL_MODE = false;

//flags for toggling visual design prototype behavior
dvt.AdfDiagramNode._CONTAINER_BADGE_MIN_ZOOM = 0;//.5;

//container chrome clip path def constants
dvt.AdfDiagramNode._CONTAINER_CHROME_CLIP_PATH_X = 0;
dvt.AdfDiagramNode._CONTAINER_CHROME_CLIP_PATH_Y = 1;
dvt.AdfDiagramNode._CONTAINER_CHROME_CLIP_PATH_W = 2;
dvt.AdfDiagramNode._CONTAINER_CHROME_CLIP_PATH_H = 3;
dvt.AdfDiagramNode._CONTAINER_CHROME_CLIP_PATH_RX = 4;
dvt.AdfDiagramNode._CONTAINER_CHROME_CLIP_PATH_RY = 5;

//Bug 16036538: flag indicating whether nodes should be moved to the front of the
//z-order when showing the selection or hover effect
dvt.AdfDiagramNode._CHANGE_Z_ORDER_ON_HIGHLIGHT = true;


// keys for retrieving style properties


/**
 * Style key for the color of the selected node state
 * @const
 */
dvt.AdfDiagramNode.SELECTED_COLOR = 'nodeSelectColor';


/**
 * Style key for the inner color of the hover node state
 * @const
 */
dvt.AdfDiagramNode.HOVER_INNER_COLOR = 'nodeHoverInnerColor';


/**
 * Style key for the outer color of the hover node state
 * @const
 */
dvt.AdfDiagramNode.HOVER_OUTER_COLOR = 'nodeHoverOuterColor';


/**
 * Style key for the node container style
 * @const
 */
dvt.AdfDiagramNode.CONTAINER_STYLE = 'nodeContainerStyle';

/**
 * Style key for the node overview style
 * @const
 */
dvt.AdfDiagramNode.OVERVIEW_STYLE = 'nodeOverviewStyle';

/**
 * Style key for the disclosed node overview style
 * @const
 */
dvt.AdfDiagramNode.OVERVIEW_CONTAINER_STYLE = 'nodeOverviewContainerStyle';


/**
 * Style key for the node label style
 * @const
 */
dvt.AdfDiagramNode.LABEL_STYLE = 'nodeLabelStyle';


/**
 * Style key for the node CSS style
 * @const
 */
dvt.AdfDiagramNode.CSS_STYLE = 'nodeCSSStyle';


/**
 * Initialization method called by the constructor
 *
 * @param {dvt.Context} context the rendering context
 * @param {dvt.AdfDiagramNodeData} nodeData the data for this node
 * @param {dvt.AdfDiagram} diagram the Diagram component that this node belongs to
 */
dvt.AdfDiagramNode.prototype.Init = function(context, nodeData, diagram)
{
  dvt.AdfDiagramNode.superclass.Init.call(this, context, nodeData.getId(), diagram);

  this._contentPane;

  this._data = nodeData;
  this._diagram = diagram;

  this._nodeDef;
  this._childNodeIds;
  this._childLinkIds;
  this._crossChildLinkIds;

  this._shortDesc;

  this._bStack = false;
  this._stackCount = 0;
  this._stackBadge;
  this._stackUnderlay;
  this._stackBadgeHandler;

  this._bPartialExpand = false;
  this._partialExpandIndicator;

  this._nodeWidth;
  this._nodeHeight;
  this._zoom = -1;
  this._bIgnoreNextRender = false;
  this._layoutBounds;

  this._overlays;
  this._label;
  //Bug 14186917: save the label dimensions, because getting
  //them dynamically seems to return different sizes based on zoom
  this._labelDims;
  this._backgroundObj;
  this._backgroundMouseEventCatcher;
  this._nodeUnderlay;

  this._hover = false;

  this._bForceZoom100Facet = false;
  this._bFlyOutEnabled = true;
  this._flyOuts;
  this._flyOutsPos;
  this._flyOutAnim;
  this._flyInAnim;
  this._bFlyInAnimCanceled = false;

  this._bDisclosed = false;
  this._bDisclosureChanged = false;
  this._childNodePane;
  this._childNodePaneDims;
  this._containerChrome;
  this._containerTopObj;
  this._containerTopBounds;
  this._containerTopAvailBounds;
  this._containerBottomObj;
  this._containerBottomBounds;
  this._containerBottomAvailBounds;
  this._containerLeftObj;
  this._containerLeftBounds;
  this._containerLeftAvailBounds;
  this._containerRightObj;
  this._containerRightBounds;
  this._containerRightAvailBounds;
  this._containerPadding = {left: dvt.AdfDiagramNode._CONTAINER_PADDING,
    right: dvt.AdfDiagramNode._CONTAINER_PADDING,
    top: dvt.AdfDiagramNode._CONTAINER_PADDING,
    bottom: dvt.AdfDiagramNode._CONTAINER_PADDING};
  this._containerUnderlay;
  this._layout;
  this._nodeParent;
  this._visibleChildNodeCount = 0;
  this._containerBadge;
  this._containerBadgeHandler;
  this._nodeWidth100;
  this._nodeHeight100;
  this._oldNodeWidth;
  this._oldNodeHeight;
  this._bHiddenAncestor = false;
  this._bHiddenAncestorChanged = false;

  this._oldLabelBounds;
  this._oldLabelPos;
  this._oldLabelRotAngle;
  this._oldLabelRotPoint;

  this._deleteEnabled = false;
  this._bHoverEffect = false;
  this._bFocused = false;
  this._bFocusEffect = false;
  this._focusUnderlay;
  this._shadow;
  //Bug 15839721: flag to prevent triggering hover effect
  this._bPreventHoverEffect = false;

  this._maxAlpha = 1.0;

  this._afRootPlaceholder;

  this._showPopupBehaviors;
  this._clientBehaviors;

  this._draggable = true;

  //panelCard
  this._stateProps = {};

  //associate node with itself so that all children automatically
  //default to the node as the logical object
  this.GetDiagram().getEventManager().associate(this, this);

  this._bFirefox = dvt.Agent.isPlatformGecko();

  //skinning
  var nodeStyles = this._diagram.getNodeStyles();
  this._selectColor = nodeStyles[dvt.AdfDiagramNode.SELECTED_COLOR];
  this._hoverInnerColor = nodeStyles[dvt.AdfDiagramNode.HOVER_INNER_COLOR];
  this._hoverOuterColor = nodeStyles[dvt.AdfDiagramNode.HOVER_OUTER_COLOR];

  var containerStyle = nodeStyles[dvt.AdfDiagramNode.CONTAINER_STYLE];
  if (containerStyle) {
    this._defaultContainerBgColor = containerStyle.getStyle(dvt.CSSStyle.BACKGROUND_COLOR);
    this._defaultContainerBorderColor = containerStyle.getStyle(dvt.CSSStyle.BORDER_COLOR);
    this._defaultContainerBorderRx = dvt.CSSStyle.toNumber(containerStyle.getStyle(dvt.CSSStyle.BORDER_RADIUS));
  }

  this._nodeLabelStyles = nodeStyles[dvt.AdfDiagramNode.LABEL_STYLE];

  this._containerStyle;
  this._overviewStyle;
  this._overviewContainerStyle;


  this._partialExpandIndicatorContainer;

  //handle tabbing inside of the node - keyboard focus
  this._tabStopsNodeArray;
  this._curTabStopIdx = -1;

  this._linkHandle;

  // the externally-specified width and height, if any
  this._width;
  this._height;
};

/**
 * @override
 */
dvt.AdfDiagramNode.prototype.GetDiagram = function()
{
  if (this._diagram) {
    return this._diagram;
  }
  if (this._cachedDiagram) {
    return this._cachedDiagram;
  }
  var parent = this.getParent();
  while (parent)
  {
    if (parent instanceof dvt.AdfDiagram)
    {
      return parent;
    }
    parent = parent.getParent();
  }
};


/**
 * @protected
 * Determine if the transient toolbar should be shown on a click instead of a
 * mouseover.
 */
dvt.AdfDiagramNode.prototype.IsTransientToolbarOnClick = function() {
  return true;
};


/**
 * Adds hover effect to the diagram node
 */
dvt.AdfDiagramNode.prototype.showHoverEffect = function() {
  //Bug 15839721: if preventing the hover effect, just return
  if (this.isHiddenAncestor() || this._bPreventHoverEffect) {
    return;
  }

  this._bHoverEffect = true;
  if (this._bHighlight) {
    this._hideFeedback();
  }
  this._showFeedback(true, this._selected);

  //link handle behavior is tied up with selection events on touch devices and with hover events on desktop
  if (!dvt.Agent.isTouchDevice())
    this.showLinkHandle();
};


/**
 * Removes hover effect from the diagram node
 */
dvt.AdfDiagramNode.prototype.hideHoverEffect = function() {
  if (this.isHiddenAncestor()) {
    return;
  }

  this._bHoverEffect = false;
  if (this._bHighlight) {
    this._hideFeedback();
  }
  if (this._bFocusEffect || this._selected) {
    this._showFeedback(this._bFocusEffect, this._selected); // keep hover effect if we're displaying keyboard focus
  }

  //link handle behavior is tied up with selection events on touch devices and with hover events on desktop
  if (!dvt.Agent.isTouchDevice())
    this.hideLinkHandle();
};
/**
 * Show node roll over effect
 *
 * @param {dvt.MouseEvent | dvt.KeyboardEvent} event the event triggering the roll over
 */
dvt.AdfDiagramNode.prototype.showRollOverEffect = function(event) {
  if (this.isHiddenAncestor()) {
    return;
  }
  //don't set flag here because when view is re-rendered when crossing
  //a zoom threshold, the node might show the hover effect by mistake
  //this._hover = true;
  this.GetDiagram().ShowNodeHoverEffect(this, event instanceof dvt.KeyboardEvent);

  //don't show the flyouts if it requires a click
  if (!this.IsTransientToolbarOnClick()) {
    if (this.isFlyOutEnabled())
    {
      this.CancelFlyInAnim();
      this.ShowFlyOuts();
    }
  }
};
/**
 * Hide node roll over effect
 *
 * @param {dvt.MouseEvent | dvt.KeyboardEvent} event the event hiding the roll over
 */
dvt.AdfDiagramNode.prototype.hideRollOverEffect = function(event) {
  //don't set flag here because when view is re-rendered when crossing
  //a zoom threshold, the node might lose the hover effect by mistake
  //this._hover = false;
  this.GetDiagram().HideNodeHoverEffect(this);

  //don't hide the flyouts if it requires a click
  if (!this.IsTransientToolbarOnClick()) {
    this.HideFlyOuts(event instanceof dvt.KeyboardEvent);
  }
};

dvt.AdfDiagramNode.prototype.showHoverDetails = function(bImmediate) {
  if (this.isHiddenAncestor()) {
    return;
  }

  this.GetDiagram().ShowNodeHoverDetails(this, bImmediate);
};

dvt.AdfDiagramNode.prototype.hideHoverDetails = function() {
  this.GetDiagram().HideNodeHoverDetails();
};

dvt.AdfDiagramNode.prototype.HandleClick = function(event) {
  if (this.isHiddenAncestor()) {
    return;
  }

  this.GetDiagram().HandleNodeClick(this, event);
};

dvt.AdfDiagramNode.prototype.HandleDblClick = function(event) {
  if (this.isHiddenAncestor()) {
    return;
  }

  this.GetDiagram().HandleNodeDblClick(this);
};

dvt.AdfDiagramNode.prototype.GetFirstShape = function(displayable)
{
  if (this.isDisclosed()) {
    return this._getFirstShape(this._containerChrome);
  }
  else {
    return this._getFirstShape(this._afRoot);
  }
};

dvt.AdfDiagramNode.prototype._getFirstShape = function(displayable)
{
  var ar = [displayable];
  while (ar.length > 0)
  {
    var disp = ar.splice(0, 1)[0];
    if (disp instanceof dvt.Shape)
    {
      return disp;
    }
    else if (disp instanceof dvt.Container)
    {
      var numChildren = disp.getNumChildren();
      for (var childIndex = 0; childIndex < numChildren; childIndex++) {
        var child = disp.getChildAt(childIndex);
        ar.push(child);
      }
    }
  }
  return null;
};

/**
 * Highlights the node - shows focus and selection effects
 * @param {boolean} bHover true to show focus effect
 * @param {boolean} bSelect true to show selection effect
 * @private
 */
dvt.AdfDiagramNode.prototype._showFeedback = function(bHover, bSelect) {
  var bOldHighlight = this._bHighlight;
  this._bHighlight = true;

  //Bug 16036538: move the node to the front of the z-order when highlighted
  if (dvt.AdfDiagramNode._CHANGE_Z_ORDER_ON_HIGHLIGHT && !this._diagram.isDragged() && this.ReorderNodes()) {
    var parent = this.GetParentContainer();
    if (parent && parent.contains(this)) {
      this.InsertNodeIntoParent(parent);
    }
  }

  if (bOldHighlight) {
    return;
  }

  this.ShowHighlight(bHover, bSelect);
};

/**
 * Returns true if z-order of nodes should change
 * @return {boolean} true if z-order of nodes should change
 * @protected
 */
dvt.AdfDiagramNode.prototype.ReorderNodes = function() {
  return this.isSelectable();
};

/**
 * Removes focus and selection effects from the node
 * @private
 */
dvt.AdfDiagramNode.prototype._hideFeedback = function()
{
  this._bHighlight = false;

  //Bug 16036538: move the node back in the z-order when unhighlighted
  if (dvt.AdfDiagramNode._CHANGE_Z_ORDER_ON_HIGHLIGHT && !this._diagram.isDragged() && this.ReorderNodes()) {
    var parent = this.GetParentContainer();
    if (parent && parent.contains(this)) {
      this.InsertNodeIntoParent(parent);
    }
  }

  this.HideHighlight();
};


/**
 * Displays hover and selection feedback for this node
 *
 * @param {boolean} bHover whether or not hover feedback should be displayed
 * @param {boolean} bSelect whether or not selection feedback should be displayed
 */
dvt.AdfDiagramNode.prototype.ShowHighlight = function(bHover, bSelect) {
  var highlightShape = this.GetHighlightShape(true);

  if (bHover != highlightShape.isHoverEffectShown()) {
    if (bHover) {
      highlightShape.showHoverEffect();
    }
    else {
      highlightShape.hideHoverEffect();
    }
  }

  highlightShape.setSelected(bSelect);
};


/**
 * Removes hover and selection feedback for this node
 */
dvt.AdfDiagramNode.prototype.HideHighlight = function() {
  var highlightShape = this.GetHighlightShape(false);
  if (highlightShape) {
    highlightShape.hideHoverEffect();
    highlightShape.setSelected(false);
  }
  //if this is a regular node, may need to remove the underlay
  if (this._nodeUnderlay) {
    this._nodeUnderlay.getParent().removeChild(this._nodeUnderlay);
    if (this._nodeUnderlay.destroy) {
      this._nodeUnderlay.destroy();
    }
    this._nodeUnderlay = null;
  }
};


/**
 * Gets the shape that should be used for rendering hover and selection feedback
 *
 * @param {boolean} bCreate whether the shape should be created if it does not currently exist
 * @return {dvt.Shape} the shape that should be used for rendering hover and selection feedback
 */
dvt.AdfDiagramNode.prototype.GetHighlightShape = function(bCreate) {
  if (!this._nodeUnderlay && bCreate) {
    this._nodeUnderlay = this.CreateUnderlay();
    this.UpdateHighlightStrokes();
    if (this._stackUnderlay) {
      // Hover/selection feedback is rendered on the first stack underlay
      this._stackUnderlay.addChildAt(this._nodeUnderlay, 2);
    }
    else {
      this._getContentContainer().addChildAt(this._nodeUnderlay, 0);
    }
  }
  return this._nodeUnderlay;
};


/**
 * Creates the underlay shape for this node for displaying hover/selection feedback
 * @return {dvt.Rect} the underlay shape for this node
 */
dvt.AdfDiagramNode.prototype.CreateUnderlay = function() {
  var dims = this.getContentBounds();
  var underlay = new dvt.Rect(this.getCtx(), dims.x, dims.y, dims.w, dims.h);
  underlay.setRx(1);
  underlay.setRy(1);
  underlay.setSolidFill(this._hoverInnerColor);
  underlay.setMouseEnabled(false);
  //for stacks, need to extend the underlay to draw around the stack underlay
  this.GrowUnderlayForStack(underlay);
  return underlay;
};


/**
 * Modifies the dimensions of the node underlay to fit the stack underlay if this node is a stack
 *
 * @param {dvt.Rect} underlay the underlay shape for this node
 */
dvt.AdfDiagramNode.prototype.GrowUnderlayForStack = function(underlay) {
  if (this.isStack()) {
    underlay.setX(underlay.getX() - dvt.AdfDiagramNode._STACK_UNDERLAY_PADDING);
    underlay.setY(underlay.getY() - dvt.AdfDiagramNode._STACK_UNDERLAY_PADDING);
    underlay.setWidth(underlay.getWidth() + 2 * dvt.AdfDiagramNode._STACK_UNDERLAY_PADDING);
    underlay.setHeight(underlay.getHeight() + 2 * dvt.AdfDiagramNode._STACK_UNDERLAY_PADDING);
  }
};


/**
 * Sets the hover and selected strokes on the highlight shape
 */
dvt.AdfDiagramNode.prototype.UpdateHighlightStrokes = function() {
  if (this._nodeUnderlay) {
    // desired border widths:
    // hover          2px inner, 2px outer
    // selected       1px inner, 2px outer
    // selectedHover  2px inner, 2px outer
    // Need to scale the stroke widths manually if we can't use non-scaling-strokes

    var scale = this._diagram.supportsVectorEffects() ? 1 : 1 / this._diagram.getPanZoomCanvas().getZoom();
    var hiw = 4 * scale;
    var how = 2 * scale;
    var siw = 2 * scale;
    var sow = 2 * scale;
    var shiw = 4 * scale;
    var show = 2 * scale;
    // actual inner border width = 2*desired inner border width
    // actual outer border width = actual inner border width + 2*desired outer border width
    var his = new dvt.SolidStroke(this._hoverInnerColor, 1, hiw);
    his.setFixedWidth(true);
    var hos = new dvt.SolidStroke(this._hoverOuterColor, 1, hiw + 2 * how);
    hos.setFixedWidth(true);
    var sis = new dvt.SolidStroke(this._hoverInnerColor, 1, siw);
    sis.setFixedWidth(true);
    var sos = new dvt.SolidStroke(this._selectColor, 1, siw + 2 * sow);
    sos.setFixedWidth(true);
    var shis = new dvt.SolidStroke(this._hoverInnerColor, 1, shiw);
    shis.setFixedWidth(true);
    var shos = new dvt.SolidStroke(this._selectColor, 1, shiw + 2 * show);
    shos.setFixedWidth(true);
    this._nodeUnderlay.setSelectedStroke(sis, sos).setHoverStroke(his, hos).setSelectedHoverStroke(shis, shos);
  }
};

/**
 * @override
 */
dvt.AdfDiagramNode.prototype.getDatatip = function() {
  if (this.isHiddenAncestor() || this._diagram.GetTransientToolbarDataId() == this.getData().getId()) {
    return null;
  }

  return this.getShortDesc();
};

/**
 * Sets short description for the node
 * @param {string} shortDesc short description for the node
 */
dvt.AdfDiagramNode.prototype.setShortDesc = function(shortDesc) {
  this._shortDesc = shortDesc;
};

/**
 * Gets short description for the node
 * @return {string}  Diagram node short description
 */
dvt.AdfDiagramNode.prototype.getShortDesc = function() {
  return this._shortDesc;
};

/**
 * Gets the map of node layout attributes.
 * @param {string} layout layout name
 * @return {object}
 */
dvt.AdfDiagramNode.prototype.getLayoutAttributes = function(layout) {
  var layoutAttrs = this.getData().getLayoutAttributes();
  if (layoutAttrs && layout) {
    return layoutAttrs[layout];
  }
  return {};
};

/**
 * Get Diagram node data
 * @return {dvt.AdfDiagramNodeData} Diagram node data
 */
dvt.AdfDiagramNode.prototype.getData = function()
{
  return this._data;
};

dvt.AdfDiagramNode.prototype.setNodeDef = function(nodeDef)
{
  this._nodeDef = nodeDef;

  if (nodeDef)
  {
    nodeDef.stamp(this.getData().getElAttributes(), this);
    this._refreshCursor();
  }
};

dvt.AdfDiagramNode.prototype.getNodeDef = function()
{
  return this._nodeDef;
};

dvt.AdfDiagramNode.prototype.setLayout = function(layout)
{
  this._layout = layout;
};

dvt.AdfDiagramNode.prototype.getLayout = function()
{
  return this._layout;
};

/**
 * Renders this node
 *
 * @param {dvt.AfContext} afContext the rendering context for the contained af components
 * @param {dvt.Animator} animator (optional) the animator into which this node should be rendered
 */
dvt.AdfDiagramNode.prototype.renderNode = function(afContext, animator)
{
  //Performance: need to use the same afContext when creating afRoot
  //Performance: need to layout and render the parsed afComponents
  //so dont delete afRoot
  //     if (this._afRoot)
  //     {
  //       this._getContentContainer().removeChild(this._afRoot);
  //       this._afRoot.destroy();
  //       delete this._afRoot;
  //     }

  //if ignoring the next render, reset the flag and return
  if (this._bIgnoreNextRender)
  {
    if (!this._diagram.supportsVectorEffects()) {
      this.RefreshSelectionEffect();
    }
    this._bIgnoreNextRender = false;
    return;
  }

  var availWidth = this.getWidth();
  var availHeight = this.getHeight();

  var childNodePaneDims = null;
  var bDisclosed = this.isDisclosed();// && this.isContainer();
  if (bDisclosed)
  {
    //if we have a saved size, use it
    //if (this._nodeWidth && this._nodeHeight)
    //{
    //  availWidth = this._nodeWidth;
    //  availHeight = this._nodeHeight;
    //}
    //else
    //{
    var bbox = this.CalcChildNodePaneBounds(animator);
    //logText(this._data._id + ": " + bbox.x + ", " + bbox.y +
    //        ", " + bbox.w + ", " + bbox.h);
    childNodePaneDims = bbox;
    //Bug 15868740: save childNodePane dims so that we can translate it if the x,y coords != 0,0
    this._childNodePaneDims = bbox;
    availWidth = bbox.w;
    availHeight = bbox.h;
    var containerPadding = this.getContainerPadding();
    if (containerPadding) {
      if (containerPadding.left === 'auto' || containerPadding.right === 'auto')
        availWidth = Number.MAX_VALUE;
      else
        availWidth += containerPadding.left + containerPadding.right;

      if (containerPadding.top === 'auto' || containerPadding.bottom === 'auto')
        availHeight = Number.MAX_VALUE;
      else
        availHeight += containerPadding.top + containerPadding.bottom;
    }
    //}
  }
  else
  {
    if (this._nodeWidth100) {
      this._nodeWidth = this._nodeWidth100;
    }
    if (this._nodeHeight100) {
      this._nodeHeight = this._nodeHeight100;
    }

    //if we have a saved size, use it
    if (this._nodeWidth)
    {
      availWidth = this._nodeWidth;
    }
    if (this._nodeHeight)
    {
      availHeight = this._nodeHeight;
    }

    //determine whether we have a specified size
    var bUnknownWidth = false;
    var bUnknownHeight = false;
    if (!availWidth)
    {
      bUnknownWidth = true;
      availWidth = Number.MAX_VALUE;
    }
    //decrease the available size based on the zoom facet
    else {
      availWidth *= this._zoom;
    }
    if (!availHeight)
    {
      bUnknownHeight = true;
      availHeight = Number.MAX_VALUE;
    }
    //decrease the available size based on the zoom facet
    else {
      availHeight *= this._zoom;
    }
  }

  //Performance: need to layout and render the parsed afComponents
  // use the same afContext when creating afRoot
  // var afContext = new dvt.AfContext(this.getCtx(), this.GetDiagram().getEventManager());
  afContext.setAvailableWidth(availWidth);
  afContext.setAvailableHeight(availHeight);
  // afContext.setELContext(this.getData().getElAttributes());

  //this._afRoot = dvt.AfComponentFactory.parseAndLayout(afContext, this.getNodeDef().getNodeTemplate(), this._contentPane);
  //add AF content of this node underneath any child nodes (in createNodeContents)
  //     this._afRoot = dvt.AfComponentFactory.parseAndLayout(afContext, this.getNodeDef().getNodeTemplate(), this);
  if (!bDisclosed && this._afRoot)
  {
    dvt.AfComponentFactory.layoutAndRender(afContext, this._afRoot);
  }

  var nodeWidth = availWidth;
  var nodeHeight = availHeight;
  this._containerTopBounds = null;
  this._containerBottomBounds = null;
  this._containerLeftBounds = null;
  this._containerRightBounds = null;
  if (bDisclosed)
  {
    //translation for childNodePane
    //Bug 15868740: translate childNodePane so that top left corner of bounding box is entirely
    //within the container, to account for any overlays that extend into negative coordinate space
    var cnpTx = -this._childNodePaneDims.x;
    var cnpTy = -this._childNodePaneDims.y;

    var defaultPadding = dvt.AdfDiagramNode._CONTAINER_PADDING;
    var minTopPadding = containerPadding.top === 'auto' ? defaultPadding : containerPadding.top;
    var minBottomPadding = containerPadding.bottom === 'auto' ? defaultPadding : containerPadding.bottom;
    var minFacetHeight = this._childNodePaneDims.h + minTopPadding + minBottomPadding;

    this._nodeHeight = minFacetHeight;
    if (this._containerLeftObj) {
      afContext.setAvailableWidth(Number.MAX_VALUE);
      afContext.setAvailableHeight(containerPadding.top === 'auto' || containerPadding.bottom === 'auto' ? Number.MAX_VALUE : this._nodeHeight);
      dvt.AfComponentFactory.layoutAndRender(afContext, this._containerLeftObj);

      //Bug 14374448 - DIAGRAMMER: <AF:OUTPUTFORMATTED> SIZES ITS CONTAINING LAYOUT INCONSISTENTLY
      // var clDims = this._containerLeftObj.getDimensions();
      var clDims = dvt.AfComponentFactory.getBounds(this._containerLeftObj);
      if (clDims.h < this._nodeHeight) { // auto padding is used, rerender for bigger size
        afContext.setAvailableHeight(this._nodeHeight);
        dvt.AfComponentFactory.layoutAndRender(afContext, this._containerLeftObj);
        clDims = dvt.AfComponentFactory.getBounds(this._containerLeftObj);
      }

      this._containerLeftBounds = clDims;
      this._nodeHeight = Math.max(clDims.h, this._nodeHeight);
      cnpTx += clDims.w;

      //need to save the available bounds given to the left to
      //workaround bug 13878434
      this._containerLeftAvailBounds = {w: Number.MAX_VALUE, h: this._nodeHeight};
    }

    if (this._containerRightObj) {
      afContext.setAvailableWidth(Number.MAX_VALUE);
      afContext.setAvailableHeight(containerPadding.top === 'auto' || containerPadding.bottom === 'auto' ? Number.MAX_VALUE : this._nodeHeight);
      dvt.AfComponentFactory.layoutAndRender(afContext, this._containerRightObj);

      //Bug 14374448 - DIAGRAMMER: <AF:OUTPUTFORMATTED> SIZES ITS CONTAINING LAYOUT INCONSISTENTLY
      // var crDims = this._containerRightObj.getDimensions();
      var crDims = dvt.AfComponentFactory.getBounds(this._containerRightObj);
      if (crDims.h < this._nodeHeight) { // auto padding is used, rerender for bigger size
        afContext.setAvailableHeight(this._nodeHeight);
        dvt.AfComponentFactory.layoutAndRender(afContext, this._containerRightObj);
        crDims = dvt.AfComponentFactory.getBounds(this._containerRightObj);
      }

      this._containerRightBounds = crDims;
      this._nodeHeight = Math.max(crDims.h, this._nodeHeight);
      //need to save the available bounds given to the right to
      //workaround bug 13878434
      this._containerRightAvailBounds = {w: Number.MAX_VALUE, h: this._nodeHeight};
    }

    // if left facet taller than min height, but shorter than node height - rerender into larger size
    if (this._containerLeftBounds &&
        this._containerLeftBounds.h < this._nodeHeight &&
        this._containerLeftBounds.h > minFacetHeight) {
      afContext.setAvailableHeight(this._nodeHeight);
      dvt.AfComponentFactory.layoutAndRender(afContext, this._containerLeftObj);
      this._containerLeftBounds = dvt.AfComponentFactory.getBounds(this._containerLeftObj);
    }

    var sideFacetWidth = (this._containerLeftBounds ? this._containerLeftBounds.w : 0) +
        (this._containerRightBounds ? this._containerRightBounds.w : 0);
    var minLeftPadding = containerPadding.left === 'auto' ? defaultPadding : containerPadding.left;
    var minRightPadding = containerPadding.right === 'auto' ? defaultPadding : containerPadding.right;
    var minFacetWidth = this._childNodePaneDims.w + minLeftPadding + minRightPadding + sideFacetWidth;
    this._nodeWidth = minFacetWidth;
    if (this._containerTopObj) {
      afContext.setAvailableWidth(containerPadding.left === 'auto' || containerPadding.right === 'auto' ? Number.MAX_VALUE : this._nodeWidth);
      afContext.setAvailableHeight(Number.MAX_VALUE);
      dvt.AfComponentFactory.layoutAndRender(afContext, this._containerTopObj);

      //Bug 14374448 - DIAGRAMMER: <AF:OUTPUTFORMATTED> SIZES ITS CONTAINING LAYOUT INCONSISTENTLY
      // var ctDims = this._containerTopObj.getDimensions();
      var ctDims = dvt.AfComponentFactory.getBounds(this._containerTopObj);
      if (ctDims.w < this._nodeWidth) { // auto padding is used, rerender for bigger size
        afContext.setAvailableWidth(this._nodeWidth);
        dvt.AfComponentFactory.layoutAndRender(afContext, this._containerTopObj);
        ctDims = dvt.AfComponentFactory.getBounds(this._containerTopObj);
      }

      this._containerTopBounds = ctDims;
      this._nodeWidth = Math.max(ctDims.w, this._nodeWidth);
      this._nodeHeight += ctDims.h;
      cnpTy += ctDims.h;
      if (this._containerLeftObj) {
        this._containerLeftObj.setTranslateY(ctDims.h);
      }
      if (this._containerRightObj) {
        this._containerRightObj.setTranslateY(ctDims.h);
      }
      //need to save the available bounds given to the top to
      //workaround bug 13878434
      this._containerTopAvailBounds = {w: this._nodeWidth, h: Number.MAX_VALUE};
    }

    if (this._containerBottomObj) {
      afContext.setAvailableWidth(containerPadding.left === 'auto' || containerPadding.right === 'auto' ? Number.MAX_VALUE : this._nodeWidth);
      afContext.setAvailableHeight(Number.MAX_VALUE);
      dvt.AfComponentFactory.layoutAndRender(afContext, this._containerBottomObj);

      //Bug 14374448 - DIAGRAMMER: <AF:OUTPUTFORMATTED> SIZES ITS CONTAINING LAYOUT INCONSISTENTLY
      // var cbDims = this._containerBottomObj.getDimensions();
      var cbDims = dvt.AfComponentFactory.getBounds(this._containerBottomObj);
      if (cbDims.w < this._nodeWidth) { // auto padding is used, rerender for bigger size
        afContext.setAvailableWidth(this._nodeWidth);
        dvt.AfComponentFactory.layoutAndRender(afContext, this._containerBottomObj);
        cbDims = dvt.AfComponentFactory.getBounds(this._containerBottomObj);
      }

      this._containerBottomBounds = cbDims;
      this._containerBottomObj.setTranslateY(this._nodeHeight); // should include container top height by now
      this._nodeWidth = Math.max(cbDims.w, this._nodeWidth);
      this._nodeHeight += cbDims.h;
      //need to save the available bounds given to the bottom to
      //workaround bug 13878434
      this._containerBottomAvailBounds = {w: this._nodeWidth, h: Number.MAX_VALUE};
    }

    // if top facet wider than min width, but shorter than bottom facet - rerender into larger size
    if (this._containerTopBounds &&
        this._containerTopBounds.w < this._nodeWidth &&
        this._containerTopBounds.w > minFacetWidth) {
      afContext.setAvailableWidth(this._nodeWidth);
      dvt.AfComponentFactory.layoutAndRender(afContext, this._containerTopObj);
      var ctDimsUpdated = dvt.AfComponentFactory.getBounds(this._containerTopObj);
      if (ctDimsUpdated.h !== this._containerTopBounds.h) { //update translationY, if containerTop vertical dims changed
        cnpTy += (ctDimsUpdated.h - this._containerTopBounds.h);
      }
      this._containerTopBounds = ctDimsUpdated;
    }

    // now when node width is known, position containerRight facet
    if (this._containerRightObj) {
      this._containerRightObj.setTranslateX(this._nodeWidth - this._containerRightBounds.w);
    }

    //update node dimensions if necessary
    var verticalFacetHeight = (this._containerTopBounds ? this._containerTopBounds.h : 0) +
        (this._containerBottomBounds ? this._containerBottomBounds.h : 0);

    if (containerPadding) {
      //update padding values
      if (containerPadding.top === 'auto' && containerPadding.bottom === 'auto') {
        containerPadding.top = containerPadding.bottom = (this._nodeHeight - this._childNodePaneDims.h - verticalFacetHeight) / 2;
      }
      else if (containerPadding.top === 'auto') {
        containerPadding.top = this._nodeHeight - this._childNodePaneDims.h - containerPadding.bottom - verticalFacetHeight;
      }
      else if (containerPadding.bottom === 'auto') {
        containerPadding.bottom = this._nodeHeight - this._childNodePaneDims.h - containerPadding.top - verticalFacetHeight;
      }

      if (containerPadding.left === 'auto' && containerPadding.right === 'auto') {
        containerPadding.left = containerPadding.right = (this._nodeWidth - this._childNodePaneDims.w - sideFacetWidth) / 2;
      }
      else if (containerPadding.left === 'auto') {
        containerPadding.left = this._nodeWidth - this._childNodePaneDims.w - containerPadding.right - sideFacetWidth;
      }
      else if (containerPadding.right === 'auto') {
        containerPadding.right = this._nodeWidth - this._childNodePaneDims.w - containerPadding.left - sideFacetWidth;
      }

      cnpTx += containerPadding.left;
      cnpTy += containerPadding.top;
    }

    if (cnpTx != this._childNodePane.getTranslateX()) {
      this._childNodePane.setTranslateX(cnpTx);
    }
    if (cnpTy != this._childNodePane.getTranslateY()) {
      this._childNodePane.setTranslateY(cnpTy);
    }

    this.RenderContainerChrome();

    //var cnpBounds = childNodePaneDims;//this._childNodePane.getDimensions(this);
    //this._childNodePane.setTranslateX(-cnpBounds.x + this.GetContainerPaddingX());
    //this._childNodePane.setTranslateY(-cnpBounds.y + this.GetContainerPaddingY());
  }
  else
  {
    //if (this._childNodePane) {
    //  this._childNodePane.setTranslateX(0);
    //  this._childNodePane.setTranslateY(0);
    //}

    //if the size wasn't originally known, save it now
    if (bUnknownWidth || bUnknownHeight)
    {
      //Bug 13376761: get bounds in parent coord system so
      //that root marker scale is taken into account

      //Bug 14374448 - DIAGRAMMER: <AF:OUTPUTFORMATTED> SIZES ITS CONTAINING LAYOUT INCONSISTENTLY
      //        var bounds = this._afRoot.getDimensions(this._afRoot.getParent());
      var bounds = dvt.AfComponentFactory.getBounds(this._afRoot, this._afRoot.getParent());

      if (bUnknownWidth)
      {
        this._nodeWidth = bounds.w;
        this._nodeWidth100 = this._nodeWidth;
      }
      if (bUnknownHeight)
      {
        this._nodeHeight = bounds.h;
        this._nodeHeight100 = this._nodeHeight;
      }
    }

    //inverse scale the content based on zoom facet to fit the full size node
    var contentScale = 1 / this._zoom;
    //Bug 13376761: take root marker scale into account
    this._afRoot.setScale(contentScale * this._afRoot.getScaleX(), contentScale * this._afRoot.getScaleY());

    //if we don't have a saved size, save it now (should be based on
    //100% zoom for initial render)
    if (!this._nodeWidth)
    {
      this._nodeWidth = availWidth;
      this._nodeWidth100 = this._nodeWidth;
    }
    if (!this._nodeHeight)
    {
      this._nodeHeight = availHeight;
      this._nodeHeight100 = this._nodeHeight;
    }

    nodeWidth = this._nodeWidth;
    nodeHeight = this._nodeHeight;
  }

  if (this._labelObj)
  {
    //don't constrain size of label to size of node
    afContext.setAvailableWidth(Number.MAX_VALUE);
    afContext.setAvailableHeight(Number.MAX_VALUE);
    dvt.AfComponentFactory.layoutAndRender(afContext, this._labelObj);

    //Bug 14186917: save the label dimensions, because getting
    //them dynamically seems to return different sizes based on zoom

    //Bug 14374448 - DIAGRAMMER: <AF:OUTPUTFORMATTED> SIZES ITS CONTAINING LAYOUT INCONSISTENTLY
    // this._labelDims = this._labelObj.getDimensions();
    this._labelDims = dvt.AfComponentFactory.getBounds(this._labelObj);
  }
  else {
    this._labelDims = null;
  }

  if (this._backgroundObj) {

    afContext.setAvailableWidth(nodeWidth);
    afContext.setAvailableHeight(nodeHeight);
    dvt.AfComponentFactory.layoutAndRender(afContext, this._backgroundObj);

    //Bug 14374448 - DIAGRAMMER: <AF:OUTPUTFORMATTED> SIZES ITS CONTAINING LAYOUT INCONSISTENTLY
    // var dimensions = this._backgroundObj.getDimensions(this);
    if (this._backgroundObj instanceof dvt.SimpleMarker || this._backgroundObj instanceof dvt.ImageMarker) {
      //Recenter and resize the background
      var backgroundStroke = this._backgroundObj.getStroke();
      var halfStrokeWidth = backgroundStroke ? backgroundStroke.getWidth() / 2 : 0;
      var cx = this._backgroundObj.getCx() - this._backgroundObj.getWidth() / 2 - halfStrokeWidth + nodeWidth / 2;
      var cy = this._backgroundObj.getCy() - this._backgroundObj.getHeight() / 2 - halfStrokeWidth + nodeHeight / 2;
      this._backgroundObj.setCenter(cx, cy, true).setSize(nodeWidth, nodeHeight);
    }
    else {
      var dimensions = dvt.AfComponentFactory.getBounds(this._backgroundObj, this);
      var backgroundMat = this._backgroundObj.getMatrix().clone();
      backgroundMat.translate(-dimensions.x, -dimensions.y);
      backgroundMat.scale(this._nodeWidth / dimensions.w, this._nodeHeight / dimensions.h);
      this._backgroundObj.setMatrix(backgroundMat);
    }
    this._backgroundMouseEventCatcher.setWidth(nodeWidth);
    this._backgroundMouseEventCatcher.setHeight(nodeHeight);
  }

  if (this._overlays)
  {
    for (var iOverlay = 0; iOverlay < this._overlays.length; iOverlay++)
    {
      var overlay = this._overlays[iOverlay]['overlay'];
      //don't constrain size of overlay to size of node
      afContext.setAvailableWidth(Number.MAX_VALUE);
      afContext.setAvailableHeight(Number.MAX_VALUE);
      dvt.AfComponentFactory.layoutAndRender(afContext, overlay);
      var overlayPos = this._overlays[iOverlay]['position'];

      //Bug 14374448 - DIAGRAMMER: <AF:OUTPUTFORMATTED> SIZES ITS CONTAINING LAYOUT INCONSISTENTLY
      // var overlayBounds = overlay.getDimensions(this);
      var overlayBounds = dvt.AfComponentFactory.getBounds(overlay, this);

      var overlayX = overlayPos.x * nodeWidth;
      var overlayY = overlayPos.y * nodeHeight;
      //position overlay markers by center
      overlayX -= .5 * overlayBounds.w;
      overlayY -= .5 * overlayBounds.h;
      var overlayMat = overlay.getMatrix().clone();
      overlayMat.translate(overlayX, overlayY);
      overlay.setMatrix(overlayMat);
    }
  } //end if (this._overlays)

  if (this._bStack) {
    // Refresh badge and underlays
    this.setStack(this._bStack);
  }
  if (this.isContainer()) {
    this.RefreshContainerBadge();
  }
  if (this._bPartialExpand) {
    // Refresh partial expand indicator
    this.setPartiallyExpanded(this._bPartialExpand);
  }

  this.addAccessibilityAttributes();
  this.RefreshSelectionEffect(); // Maintain selection state through layout change
  this.AddDrawEffects();

  //Bug 13381683: save the outer bounds of the node and its overlays
  //in order to pass them to the layout engine
  this._layoutBounds = this.CalcLayoutBounds();

  this.SetAlpha(Math.min(this.getAlpha(), this.GetMaxAlpha()));
  if (animator) {
    this.TransitionDisclosure(animator, this._bDisclosureChanged, this._savedContentState);
  }
  //Bug 13381726: bChanged flag should never be set, but still
  //need to maintain visual state for hidden ancestor whenever we render
  //Bug 13381742: only need to transition in order to hide things
  this.TransitionHiddenAncestor(this._bHiddenAncestorChanged ? animator : null, true);

  this.GetDiagram().UpdateOverviewNode(this, animator);

  this._bDisclosureChanged = false;
  this._bHiddenAncestorChanged = false;
};

/**
 * Transitions container padding, when container padding values changed
 * @param {Object} oldPadding an object that contains old padding values
 * @param {Object} newPadding an object that contains new padding values
 * @param {dvt.Animator} animator The animator into which the transitions should be rendered (optional)
 * @protected
 */
dvt.AdfDiagramNode.prototype.TransitionContainerPadding = function(oldPadding, newPadding, animator) {
  var deltaTop = 0;
  var deltaRight = 0;
  var deltaBottom = 0;
  var deltaLeft = 0;
  if (oldPadding && newPadding) {
    deltaTop = newPadding.top === 'auto' ? Number.MAX_VALUE : newPadding.top - oldPadding.top;
    deltaRight = newPadding.right === 'auto' ? Number.MAX_VALUE : newPadding.right - oldPadding.right;
    deltaBottom = newPadding.bottom === 'auto' ? Number.MAX_VALUE : newPadding.bottom - oldPadding.bottom;
    deltaLeft = newPadding.left === 'auto' ? Number.MAX_VALUE : newPadding.left - oldPadding.left;
  }
  else if (newPadding) {
    deltaTop = newPadding.top === 'auto' ? Number.MAX_VALUE : newPadding.top;
    deltaRight = newPadding.right === 'auto' ? Number.MAX_VALUE : newPadding.right;
    deltaBottom = newPadding.bottom === 'auto' ? Number.MAX_VALUE : newPadding.bottom;
    deltaLeft = newPadding.left === 'auto' ? Number.MAX_VALUE : newPadding.left;
  }
  else if (oldPadding) {
    deltaTop = -oldPadding.top;
    deltaRight = -oldPadding.right;
    deltaBottom = -oldPadding.bottom;
    deltaLeft = -oldPadding.left;
  }

  //prepare for rendering facets
  this._clearContainerFacetTabStops();
  var defaultPadding = dvt.AdfDiagramNode._CONTAINER_PADDING;
  var sideFacetWidth = (this._containerLeftBounds ? this._containerLeftBounds.w : 0) +
      (this._containerRightBounds ? this._containerRightBounds.w : 0);
  var padding = newPadding ? newPadding : {top: 0, right: 0, bottom: 0, left: 0};
  var minRightPadding = padding.right === 'auto' ? defaultPadding : padding.right;
  var minLeftPadding = padding.left === 'auto' ? defaultPadding : padding.left;
  var minWidth = this._childNodePaneDims.w + minLeftPadding + minRightPadding + sideFacetWidth;
  var minTopPadding = padding.top === 'auto' ? defaultPadding : padding.top;
  var minBottomPadding = padding.bottom === 'auto' ? defaultPadding : padding.bottom;
  var minHeight = this._childNodePaneDims.h + minTopPadding + minBottomPadding;


  // ------- Rerender top and bottom facets --------
  var oldContainerTopObj = this._containerTopObj;
  var oldContainerTopBounds = this._containerTopBounds;
  var oldContainerBottomObj = this._containerBottomObj;
  var oldContainerBottomBounds = this._containerBottomBounds;
  var deltaContainerTop = 0;
  if ((this._containerTopObj || this._containerBottomObj) && (deltaRight || deltaLeft)) {

    var containerTopInfo = null, containerBottomInfo = null;
    var preferredWidth = 0;

    //Remove old top/bottom facets and create new ones
    if (this._containerTopObj) {
      this._containerTopObj = null;
      this._containerTopBounds = null;
      oldContainerTopObj.getParent().removeChild(oldContainerTopObj);
      //Render Top facet into preferred space
      preferredWidth = deltaRight === Number.MAX_VALUE || deltaLeft === Number.MAX_VALUE ? Number.MAX_VALUE :
          this._containerTopAvailBounds.w + deltaLeft + deltaRight;
      containerTopInfo = this.FullRenderContainerFacet(this._nodeDef.getContainerTopTemplate(), 'top',
          preferredWidth,
          this._containerTopAvailBounds.h, minWidth, null);
    }
    if (this._containerBottomObj) {
      this._containerBottomObj = null;
      this._containerBottomBounds = null;
      oldContainerBottomObj.getParent().removeChild(oldContainerBottomObj);
      //Render Bottom facet into preferred space using the largest width as min
      preferredWidth = deltaRight === Number.MAX_VALUE || deltaLeft === Number.MAX_VALUE ? Number.MAX_VALUE :
          this._containerBottomAvailBounds.w + deltaLeft + deltaRight;
      containerBottomInfo = this.FullRenderContainerFacet(this._nodeDef.getContainerBottomTemplate(), 'bottom',
          preferredWidth,
          this._containerBottomAvailBounds.h,
          containerTopInfo && containerTopInfo.facetBounds.w > minWidth ? containerTopInfo.facetBounds.w : minWidth,
          null);
    }

    //rerender Top facet if necessary  - if the one of the paddings is auto and bottom facet is wider than top
    if (containerTopInfo && containerBottomInfo) {
      if (preferredWidth == Number.MAX_VALUE && containerBottomInfo.facetBounds.w > containerTopInfo.facetBounds.w && containerTopInfo.facetBounds.w > minWidth) {
        containerTopInfo.facetObj.getParent().removeChild(containerTopInfo.facetObj);
        if (containerTopInfo.facetObj.destroy) {
          containerTopInfo.facetObj.destroy();
        }
        containerTopInfo = this.FullRenderContainerFacet(this._nodeDef.getContainerTopTemplate(), 'top',
            containerBottomInfo.facetBounds.w,
            this._containerTopAvailBounds.h, null, null);
      }
    }

    //reassign and scale top facet
    if (containerTopInfo) {
      this._containerTopObj = containerTopInfo.facetObj;
      this._containerTopAvailBounds = containerTopInfo.facetAvailBounds;
      this._containerTopBounds = containerTopInfo.facetBounds;
      deltaContainerTop = this._containerTopBounds.h - oldContainerTopBounds.h;
      this._containerTopObj.setScaleX(oldContainerTopBounds.w / this._containerTopBounds.w);
      if (animator) {
        var animSx = animator.removeProp(oldContainerTopObj, oldContainerTopObj.getScaleX);
        if (animSx) {
          animator.addProp(dvt.Animator.TYPE_NUMBER, this._containerTopObj, this._containerTopObj.getScaleX, this._containerTopObj.setScaleX, animSx);
        }
      }
      if (oldContainerTopObj.destroy) {
        oldContainerTopObj.destroy();
      }
    }

    //reassign and scale bottom facet
    if (containerBottomInfo) {
      this._containerBottomObj = containerBottomInfo.facetObj;
      this._containerBottomAvailBounds = containerBottomInfo.facetAvailBounds;
      this._containerBottomBounds = containerBottomInfo.facetBounds;
      this._containerBottomObj.setScaleX(oldContainerBottomBounds.w / this._containerBottomBounds.w);
      this._containerBottomObj.setTranslate(oldContainerBottomObj.getTranslateX(), oldContainerBottomObj.getTranslateY());
      if (animator) {
        var animSx = animator.removeProp(oldContainerBottomObj, oldContainerBottomObj.getScaleX);
        if (animSx) {
          animator.addProp(dvt.Animator.TYPE_NUMBER, this._containerBottomObj, this._containerBottomObj.getScaleX, this._containerBottomObj.setScaleX, animSx);
        }
        var animTx = animator.removeProp(oldContainerBottomObj, oldContainerBottomObj.getTranslateX);
        if (animTx) {
          animator.addProp(dvt.Animator.TYPE_NUMBER, this._containerBottomObj, this._containerBottomObj.getTranslateX, this._containerBottomObj.setTranslateX, animTx);
        }
        var animTy = animator.removeProp(oldContainerBottomObj, oldContainerBottomObj.getTranslateY);
        if (animTy) {
          animator.addProp(dvt.Animator.TYPE_NUMBER, this._containerBottomObj, this._containerBottomObj.getTranslateY, this._containerBottomObj.setTranslateY, animTy);
        }
      }
      if (oldContainerBottomObj.destroy) {
        oldContainerBottomObj.destroy();
      }
    }
  }

  // ------- Rerender left and right facets --------
  var oldContainerLeftObj = this._containerLeftObj;
  var oldContainerLeftBounds = this._containerLeftBounds;
  var oldContainerRightObj = this._containerRightObj;
  var oldContainerRightBounds = this._containerRightBounds;
  if ((this._containerLeftObj || this._containerRightObj) && (deltaTop || deltaBottom)) {

    var containerLeftInfo = null, containerRightInfo = null;
    var preferredHeight = 0;

    //remove left/right facets and create new ones
    if (this._containerLeftObj) {
      this._containerLeftObj = null;
      this._containerLeftBounds = null;
      oldContainerLeftObj.getParent().removeChild(oldContainerLeftObj);
      //Render Left facet into preferred space
      preferredHeight = deltaTop === Number.MAX_VALUE || deltaBottom === Number.MAX_VALUE ? Number.MAX_VALUE :
          this._containerLeftAvailBounds.h + deltaTop + deltaBottom;
      containerLeftInfo = this.FullRenderContainerFacet(this._nodeDef.getContainerLeftTemplate(), 'left',
          this._containerLeftAvailBounds.w,
          preferredHeight, null, minHeight);
    }
    if (this._containerRightObj && (deltaTop || deltaBottom)) {
      this._containerRightObj = null;
      this._containerRightBounds = null;
      oldContainerRightObj.getParent().removeChild(oldContainerRightObj);
      //Render Bottom facet into preferred space using the largest width as min
      preferredHeight = deltaTop === Number.MAX_VALUE || deltaBottom === Number.MAX_VALUE ? Number.MAX_VALUE :
          this._containerRightAvailBounds.h + deltaTop + deltaBottom;
      containerRightInfo = this.FullRenderContainerFacet(this._nodeDef.getContainerRightTemplate(), 'right',
          this._containerRightAvailBounds.w,
          preferredHeight, null,
          containerLeftInfo && containerLeftInfo.facetBounds.h > minHeight ? containerLeftInfo.facetBounds.h : minHeight);
    }

    //rerender Left facet if necessary - the one of the paddings is auto and bottom facet is wider than top
    if (containerLeftInfo && containerRightInfo) {
      if (preferredHeight == Number.MAX_VALUE && containerRightInfo.facetBounds.h > containerLeftInfo.facetBounds.h && containerLeftInfo.facetBounds.h > minHeight) {
        containerLeftInfo.facetObj.getParent().removeChild(containerLeftInfo.facetObj);
        if (containerLeftInfo.facetObj.destroy) {
          containerLeftInfo.facetObj.destroy();
        }
        containerLeftInfo = this.FullRenderContainerFacet(this._nodeDef.getContainerLeftTemplate(), 'left',
            this._containerLeftAvailBounds.w,
            containerRightInfo.facetBounds.h, null, null);
      }
    }
    //reassign and scale left facet
    if (containerLeftInfo) {
      this._containerLeftObj = containerLeftInfo.facetObj;
      this._containerLeftAvailBounds = containerLeftInfo.facetAvailBounds;
      this._containerLeftBounds = containerLeftInfo.facetBounds;
      this._containerLeftObj.setScaleY(oldContainerLeftBounds.h / this._containerLeftBounds.h);
      this._containerLeftObj.setTranslate(oldContainerLeftObj.getTranslateX(), oldContainerLeftObj.getTranslateY());
      if (animator) {
        var animSy = animator.removeProp(oldContainerLeftObj, oldContainerLeftObj.getScaleY);
        if (animSy) {
          animator.addProp(dvt.Animator.TYPE_NUMBER, this._containerLeftObj, this._containerLeftObj.getScaleY, this._containerLeftObj.setScaleY, animSy);
        }
        var animTx = animator.removeProp(oldContainerLeftObj, oldContainerLeftObj.getTranslateX);
        if (animTx) {
          animator.addProp(dvt.Animator.TYPE_NUMBER, this._containerLeftObj, this._containerLeftObj.getTranslateX, this._containerLeftObj.setTranslateX, animTx);
        }
        var animTy = animator.removeProp(oldContainerLeftObj, oldContainerLeftObj.getTranslateY);
        if (animTy) {
          animator.addProp(dvt.Animator.TYPE_NUMBER, this._containerLeftObj, this._containerLeftObj.getTranslateY, this._containerLeftObj.setTranslateY, animTy);
        }
      }
      if (oldContainerLeftObj.destroy) {
        oldContainerLeftObj.destroy();
      }
    }
    //reassign and scale right facet
    if (containerRightInfo) {
      this._containerRightObj = containerRightInfo.facetObj;
      this._containerRightAvailBounds = containerRightInfo.facetAvailBounds;
      this._containerRightBounds = containerRightInfo.facetBounds;
      this._containerRightObj.setScaleY(oldContainerRightBounds.h / this._containerRightBounds.h);
      this._containerRightObj.setTranslate(oldContainerRightObj.getTranslateX(), oldContainerRightObj.getTranslateY());
      if (animator) {
        var animSy = animator.removeProp(oldContainerRightObj, oldContainerRightObj.getScaleY);
        if (animSy) {
          animator.addProp(dvt.Animator.TYPE_NUMBER, this._containerRightObj, this._containerRightObj.getScaleY, this._containerRightObj.setScaleY, animSy);
        }
        var animTx = animator.removeProp(oldContainerRightObj, oldContainerRightObj.getTranslateX);
        if (animTx) {
          animator.addProp(dvt.Animator.TYPE_NUMBER, this._containerRightObj, this._containerRightObj.getTranslateX, this._containerRightObj.setTranslateX, animTx);
        }
        var animTy = animator.removeProp(oldContainerRightObj, oldContainerRightObj.getTranslateY);
        if (animTy) {
          animator.addProp(dvt.Animator.TYPE_NUMBER, this._containerRightObj, this._containerRightObj.getTranslateY, this._containerRightObj.setTranslateY, animTy);
        }
      }
      if (oldContainerRightObj.destroy) {
        oldContainerRightObj.destroy();
      }
    }
  }
  //update tabstops and the last element which should be a node itself
  this._updateNodeTabStops();
  if (this._tabStopsNodeArray.length > 1)
    this._tabStopsNodeArray.push(this);

  // Update paddings and deltas
  // right and left
  var facetsMaxWidth = Math.max(this._containerTopBounds ? this._containerTopBounds.w : 0,
                                this._containerBottomBounds ? this._containerBottomBounds.w : 0);
  if (deltaRight === Number.MAX_VALUE && deltaLeft === Number.MAX_VALUE) {
    newPadding.left = newPadding.right = facetsMaxWidth > minWidth ?
                      (facetsMaxWidth - this._childNodePaneDims.w - sideFacetWidth) / 2 :
                      defaultPadding;
    deltaRight = newPadding.right - oldPadding.right;
    deltaLeft = newPadding.left - oldPadding.left;
  }
  else if (deltaRight === Number.MAX_VALUE) {
    newPadding.right = facetsMaxWidth > minWidth ?
            facetsMaxWidth - this._childNodePaneDims.w - newPadding.left - sideFacetWidth :
            defaultPadding;
    deltaRight = newPadding.right - oldPadding.right;
  }
  else if (deltaLeft === Number.MAX_VALUE) {
    newPadding.left = facetsMaxWidth > minWidth ?
            facetsMaxWidth - this._childNodePaneDims.w - newPadding.right - sideFacetWidth :
            defaultPadding;
    deltaLeft = newPadding.left - oldPadding.left;
  }

  //top and bottom
  var facetsMaxHeight = Math.max(this._containerLeftBounds ? this._containerLeftBounds.h : 0,
      this._containerRightBounds ? this._containerRightBounds.h : 0);
  if (deltaTop === Number.MAX_VALUE && deltaBottom === Number.MAX_VALUE) {
    newPadding.top = newPadding.bottom = facetsMaxHeight > minHeight ?
        (facetsMaxHeight - this._childNodePaneDims.h) / 2 :
        defaultPadding;
    deltaTop = newPadding.top - oldPadding.top;
    deltaBottom = newPadding.bottom - oldPadding.bottom;
  }
  else if (deltaTop === Number.MAX_VALUE) {
    newPadding.top = facetsMaxHeight > minHeight ?
            facetsMaxHeight - this._childNodePaneDims.h - newPadding.bottom :
            defaultPadding;
    deltaTop = newPadding.top - oldPadding.top;
  }
  else if (deltaBottom === Number.MAX_VALUE) {
    newPadding.bottom = facetsMaxHeight > minHeight ?
            facetsMaxHeight - this._childNodePaneDims.h - newPadding.top :
            defaultPadding;
    deltaBottom = newPadding.bottom - oldPadding.bottoms;
  }


  var newContainerChromeWidth;
  var newContainerChromeHeight;
  var clipPathDef;

  if (this._containerChrome) {
    if (deltaRight || deltaLeft) {
      newContainerChromeWidth = this._containerChrome.getWidth() + deltaRight + deltaLeft;
    }

    if (deltaTop || deltaBottom || deltaContainerTop) {
      newContainerChromeHeight = this._containerChrome.getHeight() + deltaTop + deltaBottom + deltaContainerTop;
    }

    if (newContainerChromeWidth || newContainerChromeHeight) {
      clipPathDef = this._getContainerChromeClipPathDef();
      clipPathDef[dvt.AdfDiagramNode._CONTAINER_CHROME_CLIP_PATH_W] = (newContainerChromeWidth ? newContainerChromeWidth : clipPathDef[dvt.AdfDiagramNode._CONTAINER_CHROME_CLIP_PATH_W]);
      clipPathDef[dvt.AdfDiagramNode._CONTAINER_CHROME_CLIP_PATH_H] = (newContainerChromeHeight ? newContainerChromeHeight : clipPathDef[dvt.AdfDiagramNode._CONTAINER_CHROME_CLIP_PATH_H]);
    }
  }

  if (animator) {
    if (this._containerChrome) {
      if (newContainerChromeWidth) {
        animator.addProp(dvt.Animator.TYPE_NUMBER, this._containerChrome, this._containerChrome.getWidth, this._containerChrome.setWidth, newContainerChromeWidth);
      }
      if (newContainerChromeHeight) {
        animator.addProp(dvt.Animator.TYPE_NUMBER, this._containerChrome, this._containerChrome.getHeight, this._containerChrome.setHeight, newContainerChromeHeight);
      }
      if (clipPathDef) {
        animator.addProp(dvt.Animator.TYPE_NUMBER_ARRAY, this, this._getContainerChromeClipPathDef, this._setContainerChromeClipPath, clipPathDef);
      }
    }

    if (this._containerLeftObj) {
      //always apply scale=1, because TransitionDisclosure may have
      //adjusted scale, too
      this._setContainerFacetScale(animator, this._containerLeftObj, 1, 1);
      if (deltaContainerTop) {
        animator.addProp(dvt.Animator.TYPE_NUMBER, this._containerLeftObj, this._containerLeftObj.getTranslateY, this._containerLeftObj.setTranslateY, this._containerLeftObj.getTranslateY() + deltaContainerTop);
      }
    }

    if (this._containerRightObj) {
      //always apply scale=1, because TransitionDisclosure may have
      //adjusted scale, too
      this._setContainerFacetScale(animator, this._containerRightObj, 1, 1);
      if (deltaRight || deltaLeft) {
        animator.addProp(dvt.Animator.TYPE_NUMBER, this._containerRightObj, this._containerRightObj.getTranslateX, this._containerRightObj.setTranslateX, this._containerRightObj.getTranslateX() + deltaRight + deltaLeft);
      }
      if (deltaContainerTop) {
        animator.addProp(dvt.Animator.TYPE_NUMBER, this._containerRightObj, this._containerRightObj.getTranslateY, this._containerRightObj.setTranslateY, this._containerRightObj.getTranslateY() + deltaContainerTop);
      }
    }

    if (this._containerTopObj) {
      //always apply scale=1, because TransitionDisclosure may have
      //adjusted scale, too
      this._setContainerFacetScale(animator, this._containerTopObj, 1, 1);
    }

    if (this._containerBottomObj) {
      //always apply scale=1, because TransitionDisclosure may have
      //adjusted scale, too
      this._setContainerFacetScale(animator, this._containerBottomObj, 1, 1);
      if (deltaTop || deltaBottom || deltaContainerTop) {
        animator.addProp(dvt.Animator.TYPE_NUMBER, this._containerBottomObj, this._containerBottomObj.getTranslateY, this._containerBottomObj.setTranslateY, this._containerBottomObj.getTranslateY() + deltaTop + deltaBottom + deltaContainerTop);
      }
    }

    if (this._childNodePane) {
      if (newPadding) {
        animator.addProp(dvt.Animator.TYPE_NUMBER, this._childNodePane, this._childNodePane.getTranslateX, this._childNodePane.setTranslateX, newPadding.left + this.getContainerLeftWidth() - this._childNodePaneDims.x);
        animator.addProp(dvt.Animator.TYPE_NUMBER, this._childNodePane, this._childNodePane.getTranslateY, this._childNodePane.setTranslateY, newPadding.top + this.getContainerTopHeight() - this._childNodePaneDims.y);
      }
      else {
        animator.addProp(dvt.Animator.TYPE_NUMBER, this._childNodePane, this._childNodePane.getTranslateX, this._childNodePane.setTranslateX, this.getContainerLeftWidth() - this._childNodePaneDims.x);
        animator.addProp(dvt.Animator.TYPE_NUMBER, this._childNodePane, this._childNodePane.getTranslateY, this._childNodePane.setTranslateY, this.getContainerTopHeight() - this._childNodePaneDims.y);
      }
    }

    /*    if (this._containerBadge) {
      if (deltaLeft || deltaRight) {
        animator.addProp(dvt.Animator.TYPE_NUMBER, this._containerBadge, this._containerBadge.getTranslateX, this._containerBadge.setTranslateX, this._containerBadge.getTranslateX() + .5 * (deltaLeft + deltaRight));
      }
      if (deltaTop || deltaBottom) {
        animator.addProp(dvt.Animator.TYPE_NUMBER, this._containerBadge, this._containerBadge.getTranslateY, this._containerBadge.setTranslateY, this._containerBadge.getTranslateY() + (deltaTop + deltaBottom));
      }
    }*/

    if (this._partialExpandIndicator) {
      if (deltaLeft || deltaRight) {
        animator.addProp(dvt.Animator.TYPE_NUMBER, this._partialExpandIndicator, this._partialExpandIndicator.getTranslateX, this._partialExpandIndicator.setTranslateX, this._partialExpandIndicator.getTranslateX() + .5 * (deltaLeft + deltaRight));
      }
      if (deltaTop || deltaBottom) {
        animator.addProp(dvt.Animator.TYPE_NUMBER, this._partialExpandIndicator, this._partialExpandIndicator.getTranslateY, this._partialExpandIndicator.setTranslateY, this._partialExpandIndicator.getTranslateY() + (deltaTop + deltaBottom));
      }
    }

    if (this._focusUnderlay && this._focusUnderlay instanceof dvt.Rect) {
      if (deltaRight || deltaLeft) {
        animator.addProp(dvt.Animator.TYPE_NUMBER, this._focusUnderlay, this._focusUnderlay.getWidth, this._focusUnderlay.setWidth, this._focusUnderlay.getWidth() + deltaRight + deltaLeft);
      }
      if (deltaTop || deltaBottom) {
        animator.addProp(dvt.Animator.TYPE_NUMBER, this._focusUnderlay, this._focusUnderlay.getHeight, this._focusUnderlay.setHeight, this._focusUnderlay.getHeight() + deltaTop + deltaBottom);
      }
    }

    //Bug 15839721: update the hover/selection underlay
    if (this._nodeUnderlay) {
      if (deltaRight || deltaLeft) {
        animator.addProp(dvt.Animator.TYPE_NUMBER, this._nodeUnderlay, this._nodeUnderlay.getWidth, this._nodeUnderlay.setWidth, this._nodeUnderlay.getWidth() + deltaRight + deltaLeft);
      }
      if (deltaTop || deltaBottom) {
        animator.addProp(dvt.Animator.TYPE_NUMBER, this._nodeUnderlay, this._nodeUnderlay.getHeight, this._nodeUnderlay.setHeight, this._nodeUnderlay.getHeight() + deltaTop + deltaBottom);
      }
    }

  } //end if (animator)
  else {
    if (this._containerChrome) {
      if (newContainerChromeWidth) {
        this._containerChrome.setWidth(newContainerChromeWidth);
      }
      if (newContainerChromeHeight) {
        this._containerChrome.setHeight(newContainerChromeHeight);
      }
      if (clipPathDef) {
        this._setContainerChromeClipPath(clipPathDef);
      }
    }

    if (this._containerLeftObj) {
      //always apply scale=1, because TransitionDisclosure may have
      //adjusted scale, too
      this._setContainerFacetScale(null, this._containerLeftObj, 1, 1);
      if (deltaContainerTop) {
        this._containerLeftObj.setTranslateY(this._containerLeftObj.getTranslateY() + deltaContainerTop);
      }
    }

    if (this._containerRightObj) {
      //always apply scale=1, because TransitionDisclosure may have
      //adjusted scale, too
      this._setContainerFacetScale(null, this._containerRightObj, 1, 1);
      if (deltaRight || deltaLeft) {
        this._containerRightObj.setTranslateX(this._containerRightObj.getTranslateX() + deltaRight + deltaLeft);
      }
      if (deltaContainerTop) {
        this._containerRightObj.setTranslateY(this._containerRightObj.getTranslateY() + deltaContainerTop);
      }
    }

    if (this._containerTopObj) {
      //always apply scale=1, because TransitionDisclosure may have
      //adjusted scale, too
      this._setContainerFacetScale(null, this._containerTopObj, 1, 1);
    }

    if (this._containerBottomObj) {
      //always apply scale=1, because TransitionDisclosure may have
      //adjusted scale, too
      this._setContainerFacetScale(null, this._containerBottomObj, 1, 1);
      if (deltaTop || deltaBottom || deltaContainerTop) {
        this._containerBottomObj.setTranslateY(this._containerBottomObj.getTranslateY() + deltaTop + deltaBottom + deltaContainerTop);
      }
    }

    if (this._childNodePane) {
      if (newPadding) {
        this._childNodePane.setTranslate(newPadding.left + this.getContainerLeftWidth() - this._childNodePaneDims.x,
            newPadding.top + this.getContainerTopHeight() - this._childNodePaneDims.y);
      }
      else {
        this._childNodePane.setTranslate(this.getContainerLeftWidth() - this._childNodePaneDims.x,
            this.getContainerTopHeight() - this._childNodePaneDims.y);
      }
    }

    /*    if (this._containerBadge) {
      if (deltaLeft || deltaRight) {
        this._containerBadge.setTranslateX(this._containerBadge.getTranslateX() + .5 * (deltaLeft + deltaRight));
      }
      if (deltaTop || deltaBottom) {
        this._containerBadge.setTranslateY(this._containerBadge.getTranslateY() + (deltaTop + deltaBottom));
      }
    }*/

    if (this._partialExpandIndicator) {
      if (deltaLeft || deltaRight) {
        this._partialExpandIndicator.setTranslateX(this._partialExpandIndicator.getTranslateX() + .5 * (deltaLeft + deltaRight));
      }
      if (deltaTop || deltaBottom) {
        this._partialExpandIndicator.setTranslateY(this._partialExpandIndicator.getTranslateY() + (deltaTop + deltaBottom));
      }
    }

    if (this._focusUnderlay && this._focusUnderlay instanceof dvt.Rect) {
      if (deltaRight || deltaLeft) {
        this._focusUnderlay.setWidth(this._focusUnderlay.getWidth() + deltaRight + deltaLeft);
      }
      if (deltaTop || deltaBottom) {
        this._focusUnderlay.setHeight(this._focusUnderlay.getHeight() + deltaTop + deltaBottom);
      }
    }

    //Bug 15839721: update the hover/selection underlay
    if (this._nodeUnderlay) {
      if (deltaRight || deltaLeft) {
        this._nodeUnderlay.setWidth(this._nodeUnderlay.getWidth() + deltaRight + deltaLeft);
      }
      if (deltaTop || deltaBottom) {
        this._nodeUnderlay.setHeight(this._nodeUnderlay.getHeight() + deltaTop + deltaBottom);
      }
    }

  }//end else

  this._nodeWidth += (deltaLeft + deltaRight);
  this._nodeHeight += (deltaTop + deltaBottom);
  if (this._layoutBounds) {
    this._layoutBounds.w += (deltaLeft + deltaRight);
    this._layoutBounds.h += (deltaTop + deltaBottom);
  }
};

dvt.AdfDiagramNode.prototype.TransitionDisclosure = function(animator, bDisclosureChanged, oldState) {
  if (animator) {

    var thisRef = this;
    var func = function() {
      thisRef.RestoreContentState(thisRef._savedContentState);
    };
    dvt.Playable.appendOnInit(animator, func);

    //Bug 15839721: temporarily prevent mouseover from triggering
    //a new hover effect until after the animation is done, because
    //it will interrupt the animation and cause the underlay size
    //to appear to jump
    if (bDisclosureChanged && this._nodeUnderlay) {
      this._bPreventHoverEffect = true;
      var hoverEffectEnaFunc = function() {
        thisRef._bPreventHoverEffect = false;
      };
      dvt.Playable.appendOnEnd(animator, hoverEffectEnaFunc);
    }

    var clipPathDef;

    if (!bDisclosureChanged) {
      if (this._containerChrome) {
        if (this._nodeWidth != oldState._oldNodeWidth) {
          animator.addProp(dvt.Animator.TYPE_NUMBER, this._containerChrome, this._containerChrome.getWidth, this._containerChrome.setWidth, this._nodeWidth);
        }
        if (this._nodeHeight != oldState._oldNodeHeight) {
          animator.addProp(dvt.Animator.TYPE_NUMBER, this._containerChrome, this._containerChrome.getHeight, this._containerChrome.setHeight, this._nodeHeight);
        }
        if ((this._nodeWidth != oldState._oldNodeWidth) || (this._nodeHeight != oldState._oldNodeHeight)) {
          // update clippath
          clipPathDef = this._getContainerChromeClipPathDef();
          clipPathDef[dvt.AdfDiagramNode._CONTAINER_CHROME_CLIP_PATH_W] = this._nodeWidth;
          clipPathDef[dvt.AdfDiagramNode._CONTAINER_CHROME_CLIP_PATH_H] = this._nodeHeight;
          animator.addProp(dvt.Animator.TYPE_NUMBER_ARRAY, this, this._getContainerChromeClipPathDef, this._setContainerChromeClipPath, clipPathDef);
        }
      }

      if (this._containerLeftObj) {
        this._setContainerFacetScale(animator, this._containerLeftObj, 1, 1);
      }

      if (this._containerRightObj) {
        this._setContainerFacetScale(animator, this._containerRightObj, 1, 1);
      }

      if (this._containerTopObj) {
        this._setContainerFacetScale(animator, this._containerTopObj, 1, 1);
      }

      if (this._containerBottomObj) {
        this._setContainerFacetScale(animator, this._containerBottomObj, 1, 1);
      }

      // if (this._childNodePane) {
    // var childNodePaneX = this._childNodePane.getTranslateX();
    // var childNodePaneY = this._childNodePane.getTranslateY();
    // if (childNodePaneX != oldState._oldChildNodePaneX) {
    // animator.addProp(dvt.Animator.TYPE_NUMBER, this._childNodePane, this._childNodePane.getTranslateX, this._childNodePane.setTranslateX, childNodePaneX);
    // }
    // if (childNodePaneY != oldState._oldChildNodePaneY) {
    // animator.addProp(dvt.Animator.TYPE_NUMBER, this._childNodePane, this._childNodePane.getTranslateY, this._childNodePane.setTranslateY, childNodePaneY);
    // }
      // }
    }
    //disclosure changed
    else {
      //container is opening
      if (this.isDisclosed()) {
        var placeholderIndex = this._getContentContainer().getChildIndex(this._afRootPlaceholder);
        this._getContentContainer().removeChildAt(placeholderIndex);
        this._getContentContainer().addChildAt(oldState._afRoot, placeholderIndex);
        if (oldState._containerUnderlay) {
          this._getContentContainer().addChildAt(oldState._containerUnderlay, 0);
        }

        var sx = oldState._oldNodeWidth / this._nodeWidth;
        var sy = oldState._oldNodeHeight / this._nodeHeight;

        //set the initial state in the animator onInit, because setting
        //it immediately here can result in child node layout positioning
        //them outside the container (not sure why...maybe a scale issue?)
        func = function() {
          thisRef._containerChrome.setAlpha(0);
          thisRef._childNodePane.setScale(sx, sy);
          if (thisRef._containerLeftObj) {
            thisRef._setContainerFacetScale(null, thisRef._containerLeftObj, thisRef._containerLeftObj.getScaleX() * sx, thisRef._containerLeftObj.getScaleY() * sy);
            var tx = thisRef._containerLeftObj.getTranslateX() * sx;
            var ty = thisRef._containerLeftObj.getTranslateY() * sy;
            thisRef._setContainerFacetTranslate(null, thisRef._containerLeftObj, tx, ty);
          }
          if (thisRef._containerRightObj) {
            thisRef._setContainerFacetScale(null, thisRef._containerRightObj, thisRef._containerRightObj.getScaleX() * sx, thisRef._containerRightObj.getScaleY() * sy);
            var tx = thisRef._containerRightObj.getTranslateX() * sx;
            var ty = thisRef._containerRightObj.getTranslateY() * sy;
            thisRef._setContainerFacetTranslate(null, thisRef._containerRightObj, tx, ty);
          }
          if (thisRef._containerTopObj) {
            thisRef._setContainerFacetScale(null, thisRef._containerTopObj, thisRef._containerTopObj.getScaleX() * sx, thisRef._containerTopObj.getScaleY() * sy);
            var tx = thisRef._containerTopObj.getTranslateX() * sx;
            var ty = thisRef._containerTopObj.getTranslateY() * sy;
            thisRef._setContainerFacetTranslate(null, thisRef._containerTopObj, tx, ty);
          }
          if (thisRef._containerBottomObj) {
            thisRef._setContainerFacetScale(null, thisRef._containerBottomObj, thisRef._containerBottomObj.getScaleX() * sx, thisRef._containerBottomObj.getScaleY() * sy);
            var tx = thisRef._containerBottomObj.getTranslateX() * sx;
            var ty = thisRef._containerBottomObj.getTranslateY() * sy;
            thisRef._setContainerFacetTranslate(null, thisRef._containerBottomObj, tx, ty);
          }
        };
        dvt.Playable.appendOnInit(animator, func);

        animator.addProp(dvt.Animator.TYPE_NUMBER, this._containerChrome, this._containerChrome.getWidth, this._containerChrome.setWidth, this._nodeWidth);
        animator.addProp(dvt.Animator.TYPE_NUMBER, this._containerChrome, this._containerChrome.getHeight, this._containerChrome.setHeight, this._nodeHeight);

        clipPathDef = this._getContainerChromeClipPathDef();
        clipPathDef[dvt.AdfDiagramNode._CONTAINER_CHROME_CLIP_PATH_W] = this._nodeWidth;
        clipPathDef[dvt.AdfDiagramNode._CONTAINER_CHROME_CLIP_PATH_H] = this._nodeHeight;
        animator.addProp(dvt.Animator.TYPE_NUMBER_ARRAY, this, this._getContainerChromeClipPathDef, this._setContainerChromeClipPath, clipPathDef);

        animator.addProp(dvt.Animator.TYPE_NUMBER, this._containerChrome, this._containerChrome.getAlpha, this._containerChrome.setAlpha, 1);

        if (this._containerLeftObj) {
          this._setContainerFacetScale(animator, this._containerLeftObj, 1, 1);
          this._setContainerFacetTranslate(animator, this._containerLeftObj, this._containerLeftObj.getTranslateX(), this._containerLeftObj.getTranslateY());
        }
        if (this._containerRightObj) {
          this._setContainerFacetScale(animator, this._containerRightObj, 1, 1);
          this._setContainerFacetTranslate(animator, this._containerRightObj, this._containerRightObj.getTranslateX(), this._containerRightObj.getTranslateY());
        }
        if (this._containerTopObj) {
          this._setContainerFacetScale(animator, this._containerTopObj, 1, 1);
          this._setContainerFacetTranslate(animator, this._containerTopObj, this._containerTopObj.getTranslateX(), this._containerTopObj.getTranslateY());
        }
        if (this._containerBottomObj) {
          this._setContainerFacetScale(animator, this._containerBottomObj, 1, 1);
          this._setContainerFacetTranslate(animator, this._containerBottomObj, this._containerBottomObj.getTranslateX(), this._containerBottomObj.getTranslateY());
        }

        animator.addProp(dvt.Animator.TYPE_NUMBER, this._childNodePane, this._childNodePane.getScaleX, this._childNodePane.setScaleX, 1);
        animator.addProp(dvt.Animator.TYPE_NUMBER, this._childNodePane, this._childNodePane.getScaleY, this._childNodePane.setScaleY, 1);
        //this._childNodePane.setAlpha(0);
        //animator.addProp(dvt.Animator.TYPE_NUMBER, this._childNodePane, this._childNodePane.getAlpha, this._childNodePane.setAlpha, 1);

        if (oldState._afRoot) {
          animator.addProp(dvt.Animator.TYPE_NUMBER, oldState._afRoot, oldState._afRoot.getAlpha, oldState._afRoot.setAlpha, 0);
        }

        if (oldState._containerUnderlay) {
          animator.addProp(dvt.Animator.TYPE_NUMBER, oldState._containerUnderlay, oldState._containerUnderlay.getAlpha, oldState._containerUnderlay.setAlpha, 0);
        }

        func = function() {
          thisRef.DestroyUndisclosedContent(oldState);
        };
        dvt.Playable.appendOnEnd(animator, func);
      } //end container is opening
      //container is closing
      else {
        if (this._stackUnderlay) {
          this._getContentContainer().addChildAt(oldState._containerChrome, 1);
        }
        else {
          this._getContentContainer().addChildAt(oldState._containerChrome, 0);
        }

        this.addChild(this._childNodePane);

        var sx = this._nodeWidth / oldState._oldNodeWidth;
        var sy = this._nodeHeight / oldState._oldNodeHeight;
        animator.addProp(dvt.Animator.TYPE_NUMBER, oldState._containerChrome, oldState._containerChrome.getWidth, oldState._containerChrome.setWidth, this._nodeWidth);
        animator.addProp(dvt.Animator.TYPE_NUMBER, oldState._containerChrome, oldState._containerChrome.getHeight, oldState._containerChrome.setHeight, this._nodeHeight);
        if (oldState._containerChrome) {
          clipPathDef = this._getContainerChromeClipPathDef.call(oldState);
          clipPathDef[dvt.AdfDiagramNode._CONTAINER_CHROME_CLIP_PATH_W] = this._nodeWidth;
          clipPathDef[dvt.AdfDiagramNode._CONTAINER_CHROME_CLIP_PATH_H] = this._nodeHeight;
          animator.addProp(dvt.Animator.TYPE_NUMBER_ARRAY, oldState, this._getContainerChromeClipPathDef, this._setContainerChromeClipPath, clipPathDef);
        }
        animator.addProp(dvt.Animator.TYPE_NUMBER, oldState._containerChrome, oldState._containerChrome.getAlpha, oldState._containerChrome.setAlpha, 0);
        if (oldState._containerLeftObj) {
          this._setContainerFacetScale(animator, oldState._containerLeftObj, sx, sy);
          var tx = oldState._containerLeftObj.getTranslateX() * sx;
          var ty = oldState._containerLeftObj.getTranslateY() * sy;
          this._setContainerFacetTranslate(animator, oldState._containerLeftObj, tx, ty);
        }
        if (oldState._containerRightObj) {
          this._setContainerFacetScale(animator, oldState._containerRightObj, sx, sy);
          var tx = oldState._containerRightObj.getTranslateX() * sx;
          var ty = oldState._containerRightObj.getTranslateY() * sy;
          this._setContainerFacetTranslate(animator, oldState._containerRightObj, tx, ty);
        }
        if (oldState._containerTopObj) {
          this._setContainerFacetScale(animator, oldState._containerTopObj, sx, sy);
          var tx = oldState._containerTopObj.getTranslateX() * sx;
          var ty = oldState._containerTopObj.getTranslateY() * sy;
          this._setContainerFacetTranslate(animator, oldState._containerTopObj, tx, ty);
        }
        if (oldState._containerBottomObj) {
          this._setContainerFacetScale(animator, oldState._containerBottomObj, sx, sy);
          var tx = oldState._containerBottomObj.getTranslateX() * sx;
          var ty = oldState._containerBottomObj.getTranslateY() * sy;
          this._setContainerFacetTranslate(animator, oldState._containerBottomObj, tx, ty);
        }
        animator.addProp(dvt.Animator.TYPE_NUMBER, this._childNodePane, this._childNodePane.getScaleX, this._childNodePane.setScaleX, sx);
        animator.addProp(dvt.Animator.TYPE_NUMBER, this._childNodePane, this._childNodePane.getScaleY, this._childNodePane.setScaleY, sy);
        animator.addProp(dvt.Animator.TYPE_NUMBER, this._childNodePane, this._childNodePane.getAlpha, this._childNodePane.setAlpha, 0);

        if (this._afRoot) {
          this._afRoot.setAlpha(0);
          animator.addProp(dvt.Animator.TYPE_NUMBER, this._afRoot, this._afRoot.getAlpha, this._afRoot.setAlpha, 1);
        }

        if (this._containerUnderlay) {
          this._afRoot.setAlpha(0);
          animator.addProp(dvt.Animator.TYPE_NUMBER, this._containerUnderlay, this._containerUnderlay.getAlpha, this._containerUnderlay.setAlpha, 1);
        }

        func = function() {
          thisRef.DestroyDisclosedContent(oldState);
          thisRef._childNodePane.setAlpha(1);
          thisRef._childNodePane.setScale(1, 1);
        };
        dvt.Playable.appendOnEnd(animator, func);
      } //end container is closing
    }

    if (this._containerBadge) {
      var containerBadgeX = this._containerBadge.getTranslateX();
      var containerBadgeY = this._containerBadge.getTranslateY();
      if (containerBadgeX != oldState._oldContainerBadgeX) {
        animator.addProp(dvt.Animator.TYPE_NUMBER, this._containerBadge, this._containerBadge.getTranslateX, this._containerBadge.setTranslateX, containerBadgeX);
      }
      if (containerBadgeY != oldState._oldContainerBadgeY) {
        animator.addProp(dvt.Animator.TYPE_NUMBER, this._containerBadge, this._containerBadge.getTranslateY, this._containerBadge.setTranslateY, containerBadgeY);
      }
    }

    if (this._partialExpandIndicator) {
      var partialExpandIndicatorX = this._partialExpandIndicator.getTranslateX();
      var partialExpandIndicatorY = this._partialExpandIndicator.getTranslateY();
      if (partialExpandIndicatorX != oldState._oldPartialExpandIndicatorX) {
        animator.addProp(dvt.Animator.TYPE_NUMBER, this._partialExpandIndicator, this._partialExpandIndicator.getTranslateX, this._partialExpandIndicator.setTranslateX, partialExpandIndicatorX);
      }
      if (partialExpandIndicatorY != oldState._oldPartialExpandIndicatorY) {
        animator.addProp(dvt.Animator.TYPE_NUMBER, this._partialExpandIndicator, this._partialExpandIndicator.getTranslateY, this._partialExpandIndicator.setTranslateY, partialExpandIndicatorY);
      }
    }

    //BUG 16776913 - HTML5 HV: JS ERROR AFTER SWITCHING TO HIER_HORZ_LEFT LAYOUT
    if (this._focusUnderlay && this._focusUnderlay instanceof dvt.Rect) {
      var focusUnderlayX = this._focusUnderlay.getX();
      var focusUnderlayY = this._focusUnderlay.getY();
      var focusUnderlayWidth = this._focusUnderlay.getWidth();
      var focusUnderlayHeight = this._focusUnderlay.getHeight();
      if (focusUnderlayX != oldState._oldFocusUnderlayX) {
        animator.addProp(dvt.Animator.TYPE_NUMBER, this._focusUnderlay, this._focusUnderlay.getX, this._focusUnderlay.setX, focusUnderlayX);
      }
      if (focusUnderlayY != oldState._oldFocusUnderlayY) {
        animator.addProp(dvt.Animator.TYPE_NUMBER, this._focusUnderlay, this._focusUnderlay.getY, this._focusUnderlay.setY, focusUnderlayY);
      }
      if (focusUnderlayWidth != oldState._oldFocusUnderlayWidth) {
        animator.addProp(dvt.Animator.TYPE_NUMBER, this._focusUnderlay, this._focusUnderlay.getWidth, this._focusUnderlay.setWidth, focusUnderlayWidth);
      }
      if (focusUnderlayHeight != oldState._oldFocusUnderlayHeight) {
        animator.addProp(dvt.Animator.TYPE_NUMBER, this._focusUnderlay, this._focusUnderlay.getHeight, this._focusUnderlay.setHeight, focusUnderlayHeight);
      }
    }

    //Bug 15839721: update the hover/selection underlay
    if (this._nodeUnderlay) {
      var nodeUnderlayX = this._nodeUnderlay.getX();
      var nodeUnderlayY = this._nodeUnderlay.getY();
      var nodeUnderlayWidth = this._nodeUnderlay.getWidth();
      var nodeUnderlayHeight = this._nodeUnderlay.getHeight();
      if (nodeUnderlayX != oldState._oldNodeUnderlayX) {
        animator.addProp(dvt.Animator.TYPE_NUMBER, this._nodeUnderlay, this._nodeUnderlay.getX, this._nodeUnderlay.setX, nodeUnderlayX);
      }
      if (nodeUnderlayY != oldState._oldNodeUnderlayY) {
        animator.addProp(dvt.Animator.TYPE_NUMBER, this._nodeUnderlay, this._nodeUnderlay.getY, this._nodeUnderlay.setY, nodeUnderlayY);
      }
      if (nodeUnderlayWidth != oldState._oldNodeUnderlayWidth) {
        animator.addProp(dvt.Animator.TYPE_NUMBER, this._nodeUnderlay, this._nodeUnderlay.getWidth, this._nodeUnderlay.setWidth, nodeUnderlayWidth);
      }
      if (nodeUnderlayHeight != oldState._oldNodeUnderlayHeight) {
        animator.addProp(dvt.Animator.TYPE_NUMBER, this._nodeUnderlay, this._nodeUnderlay.getHeight, this._nodeUnderlay.setHeight, nodeUnderlayHeight);
      }
    }
  }
};


/**
 * Handles rendering transition for nodes that are/were hidden ancestors via drilling
 * @param {dvt.Animator} animator The animator into which the transitions should be rendered (optional)
 * @param {boolean} bHideOnly indicates whether to only process hide phase transitions
 */
dvt.AdfDiagramNode.prototype.TransitionHiddenAncestor = function(animator, bHideOnly) {
  //Bug 13381742: if we only need to transition in order to hide
  //things, but this node isn't a hidden ancestor, then don't do anything
  if (bHideOnly && !this.isHiddenAncestor()) {
    return;
  }

  var alpha = this.isHiddenAncestor() ? 0 : 1;
  var bMouseEnabled = !this.isHiddenAncestor();

  if (this._stackUnderlay) {
    if (animator) {
      this._stackUnderlay.setAlpha(1 - alpha);
      animator.addProp(dvt.Animator.TYPE_NUMBER, this._stackUnderlay, this._stackUnderlay.getAlpha, this._stackUnderlay.setAlpha, alpha);
    }
    else {
      this._stackUnderlay.setAlpha(alpha);
    }
  }
  if (this._stackBadge) {
    if (animator) {
      this._stackBadge.setAlpha(1 - alpha);
      animator.addProp(dvt.Animator.TYPE_NUMBER, this._stackBadge, this._stackBadge.getAlpha, this._stackBadge.setAlpha, alpha);
    }
    else {
      this._stackBadge.setAlpha(alpha);
    }
  }
  if (this._afRoot) {
    this._afRoot.setMouseEnabled(bMouseEnabled);
    if (animator) {
      this._afRoot.setAlpha(1 - alpha);
      animator.addProp(dvt.Animator.TYPE_NUMBER, this._afRoot, this._afRoot.getAlpha, this._afRoot.setAlpha, alpha);
    }
    else {
      this._afRoot.setAlpha(alpha);
    }
  }
  if (this._containerBadge) {
    this._containerBadge.setMouseEnabled(bMouseEnabled);
    if (animator) {
      this._containerBadge.setAlpha(1 - alpha);
      animator.addProp(dvt.Animator.TYPE_NUMBER, this._containerBadge, this._containerBadge.getAlpha, this._containerBadge.setAlpha, alpha);
    }
    else {
      this._containerBadge.setAlpha(alpha);
    }
  }
  if (this._partialExpandIndicator) {
    this._partialExpandIndicator.setMouseEnabled(bMouseEnabled);
    if (animator) {
      this._partialExpandIndicator.setAlpha(1 - alpha);
      animator.addProp(dvt.Animator.TYPE_NUMBER, this._partialExpandIndicator, this._partialExpandIndicator.getAlpha, this._partialExpandIndicator.setAlpha, alpha);
    }
    else {
      this._partialExpandIndicator.setAlpha(alpha);
    }
  }
  if (this._containerChrome) {
    //Bug 15838130: disable mouse events on container chrome when
    //container is drill ancestor
    this._containerChrome.setMouseEnabled(bMouseEnabled);
    if (animator) {
      this._containerChrome.setAlpha(1 - alpha);
      animator.addProp(dvt.Animator.TYPE_NUMBER, this._containerChrome, this._containerChrome.getAlpha, this._containerChrome.setAlpha, alpha);
    }
    else {
      this._containerChrome.setAlpha(alpha);
    }
  }
  if (this._labelObj) {
    this._labelObj.setMouseEnabled(bMouseEnabled);
    if (animator) {
      this._labelObj.setAlpha(1 - alpha);
      animator.addProp(dvt.Animator.TYPE_NUMBER, this._labelObj, this._labelObj.getAlpha, this._labelObj.setAlpha, alpha);
    }
    else {
      this._labelObj.setAlpha(alpha);
    }
  }
  if (this._overlays) {
    for (var iOverlay = 0; iOverlay < this._overlays.length; iOverlay++) {
      var overlay = this._overlays[iOverlay]['overlay'];
      overlay.setMouseEnabled(bMouseEnabled);
      if (animator) {
        overlay.setAlpha(1 - alpha);
        animator.addProp(dvt.Animator.TYPE_NUMBER, overlay, overlay.getAlpha, overlay.setAlpha, alpha);
      }
      else {
        overlay.setAlpha(alpha);
      }
    }
  }
  //Bug 15838130: hide node underlay when container is drill ancestor
  if (this._nodeUnderlay) {
    if (animator) {
      this._nodeUnderlay.setAlpha(1 - alpha);
      animator.addProp(dvt.Animator.TYPE_NUMBER, this._nodeUnderlay, this._nodeUnderlay.getAlpha, this._nodeUnderlay.setAlpha, alpha);
    }
    else {
      this._nodeUnderlay.setAlpha(alpha);
    }
  }
};

dvt.AdfDiagramNode.prototype.SaveContentState = function() {
  var obj = {};

  obj._containerChrome = this._containerChrome;
  obj._containerTopObj = this._containerTopObj;
  obj._containerTopBounds = this._containerTopBounds;
  obj._containerBottomObj = this._containerBottomObj;
  obj._containerBottomBounds = this._containerBottomBounds;
  obj._containerLeftObj = this._containerLeftObj;
  obj._containerLeftBounds = this._containerLeftBounds;
  obj._containerRightObj = this._containerRightObj;
  obj._containerRightBounds = this._containerRightBounds;
  obj._containerUnderlay = this._containerUnderlay;
  obj._afRoot = this._afRoot;

  if (this._nodeWidth && this._nodeHeight) {
    obj._oldNodeWidth = this._nodeWidth;
    obj._oldNodeHeight = this._nodeHeight;
  }
  else if (this._oldNodeWidth && this._oldNodeHeight) {
    obj._oldNodeWidth = this._oldNodeWidth;
    obj._oldNodeHeight = this._oldNodeHeight;
  }

  // if (this._childNodePane) {
  // obj._oldChildNodePaneX = this._childNodePane.getTranslateX();
  // obj._oldChildNodePaneY = this._childNodePane.getTranslateY();
  // }

  if (this._containerBadge) {
    obj._oldContainerBadgeX = this._containerBadge.getTranslateX();
    obj._oldContainerBadgeY = this._containerBadge.getTranslateY();
  }

  if (this._partialExpandIndicator) {
    obj._oldPartialExpandIndicatorX = this._partialExpandIndicator.getTranslateX();
    obj._oldPartialExpandIndicatorY = this._partialExpandIndicator.getTranslateY();
  }

  //BUG 16776913 - HTML5 HV: JS ERROR AFTER SWITCHING TO HIER_HORZ_LEFT LAYOUT
  if (this._focusUnderlay && this._focusUnderlay instanceof dvt.Rect) {
    obj._oldFocusUnderlayX = this._focusUnderlay.getX();
    obj._oldFocusUnderlayY = this._focusUnderlay.getY();
    obj._oldFocusUnderlayWidth = this._focusUnderlay.getWidth();
    obj._oldFocusUnderlayHeight = this._focusUnderlay.getHeight();
  }

  //Bug 15839721: save the hover/selection underlay state
  if (this._nodeUnderlay) {
    obj._oldNodeUnderlayX = this._nodeUnderlay.getX();
    obj._oldNodeUnderlayY = this._nodeUnderlay.getY();
    obj._oldNodeUnderlayWidth = this._nodeUnderlay.getWidth();
    obj._oldNodeUnderlayHeight = this._nodeUnderlay.getHeight();
  }

  return obj;
};

dvt.AdfDiagramNode.prototype.RestoreContentState = function(obj) {
  if (this._containerChrome) {
    this._containerChrome.setWidth(obj._oldNodeWidth);
    this._containerChrome.setHeight(obj._oldNodeHeight);

    var clipPathDef = this._getContainerChromeClipPathDef();
    clipPathDef[dvt.AdfDiagramNode._CONTAINER_CHROME_CLIP_PATH_W] = obj._oldNodeWidth;
    clipPathDef[dvt.AdfDiagramNode._CONTAINER_CHROME_CLIP_PATH_H] = obj._oldNodeHeight;
    this._setContainerChromeClipPath(clipPathDef);
  }

  if (this._containerTopObj) {
    if (obj._containerTopBounds && this._containerTopBounds) {
      this._setContainerFacetScale(null,
                                   this._containerTopObj,
                                   obj._containerTopBounds.w / this._containerTopBounds.w,
                                   obj._containerTopBounds.h / this._containerTopBounds.h);
    }
  }
  if (this._containerBottomObj) {
    if (obj._containerBottomBounds && this._containerBottomBounds) {
      this._setContainerFacetScale(null,
                                   this._containerBottomObj,
                                   obj._containerBottomBounds.w / this._containerBottomBounds.w,
                                   obj._containerBottomBounds.h / this._containerBottomBounds.h);
    }
  }
  if (this._containerLeftObj) {
    if (obj._containerLeftBounds && this._containerLeftBounds) {
      this._setContainerFacetScale(null,
                                   this._containerLeftObj,
                                   obj._containerLeftBounds.w / this._containerLeftBounds.w,
                                   obj._containerLeftBounds.h / this._containerLeftBounds.h);
    }
  }
  if (this._containerRightObj) {
    if (obj._containerRightBounds && this._containerRightBounds) {
      this._setContainerFacetScale(null,
                                   this._containerRightObj,
                                   obj._containerRightBounds.w / this._containerRightBounds.w,
                                   obj._containerRightBounds.h / this._containerRightBounds.h);
    }
  }

  // if (this._childNodePane) {
  // this._childNodePane.setTranslateX(obj._oldChildNodePaneX);
  // this._childNodePane.setTranslateY(obj._oldChildNodePaneY);
  // }

  if (this._containerBadge) {
    this._containerBadge.setTranslate(obj._oldContainerBadgeX, obj._oldContainerBadgeY);
  }

  if (this._partialExpandIndicator) {
    this._partialExpandIndicator.setTranslate(obj._oldPartialExpandIndicatorX, obj._oldPartialExpandIndicatorY);
  }

  //BUG 16776913 - HTML5 HV: JS ERROR AFTER SWITCHING TO HIER_HORZ_LEFT LAYOUT
  if (this._focusUnderlay && this._focusUnderlay instanceof dvt.Rect) {
    if (!isNaN(obj._oldFocusUnderlayX)) {
      this._focusUnderlay.setX(obj._oldFocusUnderlayX);
    }
    if (!isNaN(obj._oldFocusUnderlayY)) {
      this._focusUnderlay.setY(obj._oldFocusUnderlayY);
    }
    if (!isNaN(obj._oldFocusUnderlayWidth)) {
      this._focusUnderlay.setWidth(obj._oldFocusUnderlayWidth);
    }
    if (!isNaN(obj._oldFocusUnderlayHeight)) {
      this._focusUnderlay.setHeight(obj._oldFocusUnderlayHeight);
    }
  }

  //Bug 15839721: restore the hover/selection underlay state
  if (this._nodeUnderlay) {
    if (!isNaN(obj._oldNodeUnderlayX)) {
      this._nodeUnderlay.setX(obj._oldNodeUnderlayX);
    }
    if (!isNaN(obj._oldNodeUnderlayY)) {
      this._nodeUnderlay.setY(obj._oldNodeUnderlayY);
    }
    if (!isNaN(obj._oldNodeUnderlayWidth)) {
      this._nodeUnderlay.setWidth(obj._oldNodeUnderlayWidth);
    }
    if (!isNaN(obj._oldNodeUnderlayHeight)) {
      this._nodeUnderlay.setHeight(obj._oldNodeUnderlayHeight);
    }
  }
};

dvt.AdfDiagramNode.prototype.addChildNode = function(node)
{
  if (!this._childNodeIds)
  {
    this._childNodeIds = [];
  }
  this._childNodeIds.push(node.getData().getId());

  if (!this._childNodePane)
  {
    this._childNodePane = new dvt.Container(this.getCtx(), this.getData().getId() + '_cnp');
    // Create a content container for everything else to make alpha fading simpler
    this._contentPane = new dvt.Container(this.getCtx(), this.getData().getId() + '_cp');
    // Transfer existing children
    var childCount = this.getNumChildren();
    for (var i = childCount - 1; i > -1; i--) {
      this._contentPane.addChildAt(this.getChildAt(i), 0); // Looping backwards so add to front
    }
    this.addChild(this._contentPane);
    this.addChild(this._childNodePane);
  }
  this._childNodePane.addChild(node);
  //this.addChild(node);

  this._visibleChildNodeCount++;
  node._nodeParent = this._childNodePane;
};

dvt.AdfDiagramNode.prototype.addChildLink = function(link)
{
  if (!this._childLinkIds)
  {
    this._childLinkIds = [];
  }
  this._childLinkIds.push(link.getData().getId());

  this._childNodePane.addChildAt(link, 0);
  link._linkParent = this._childNodePane;
  link._bCrossContainerLink = false;
};

dvt.AdfDiagramNode.prototype.addCrossChildLink = function(link)
{
  if (!this._crossChildLinkIds)
  {
    this._crossChildLinkIds = [];
  }
  this._crossChildLinkIds.push(link.getData().getId());

  this._childNodePane.addChild(link);
  link._linkParent = this._childNodePane;
  link._bCrossContainerLink = true;
};

dvt.AdfDiagramNode.prototype.getChildNodeIds = function()
{
  return this._childNodeIds;
};

dvt.AdfDiagramNode.prototype.getChildNodeCount = function()
{
  if (this._childNodeIds)
  {
    return this._childNodeIds.length;
  }
  return 0;
};

dvt.AdfDiagramNode.prototype.setVisibleChildNodeCount = function(count)
{
  this._visibleChildNodeCount = count;

  this.RefreshContainerBadge();
};

dvt.AdfDiagramNode.prototype.getVisibleChildNodeCount = function()
{
  return this._visibleChildNodeCount;
};

dvt.AdfDiagramNode.prototype.getChildLinkIds = function()
{
  return this._childLinkIds;
};

dvt.AdfDiagramNode.prototype.getCrossChildLinkIds = function()
{
  return this._crossChildLinkIds;
};

dvt.AdfDiagramNode.prototype.getChildLinkCount = function()
{
  if (this._childLinkIds)
  {
    return this._childLinkIds.length;
  }
  return 0;
};

dvt.AdfDiagramNode.prototype.getCrossChildLinkCount = function()
{
  if (this._crossChildLinkIds)
  {
    return this._crossChildLinkIds.length;
  }
  return 0;
};

dvt.AdfDiagramNode.prototype.isContainer = function()
{
  return this.getData().isContainer() && (!this.getData().isChildrenFetched() || this.getChildNodeCount() > 0);
};

/**
 * @override
 */
dvt.AdfDiagramNode.prototype.setDisclosed = function(bDisclosed)
{
  if (!this.isContainer()) {
    return;
  }

  if (bDisclosed != this._bDisclosed) {
    this._oldNodeWidth = this._nodeWidth;
    this._oldNodeHeight = this._nodeHeight;
    this._nodeWidth = null;
    this._nodeHeight = null;
    this._bDisclosureChanged = true;
    //Store the state of disclosed flag before changing it
    this.GetDiagram().setAutomationState(this.getData().getId(), 'disclosed', this._bDisclosed);
  }

  this._bDisclosed = bDisclosed;
};

/**
 * Check if the diagram node is disclosed
 * @return {boolean}  true if the diagram node is disclosed
 */
dvt.AdfDiagramNode.prototype.isDisclosed = function()
{
  return this._bDisclosed;
};

/**
 * @override
 */
dvt.AdfDiagramNode.prototype.setHiddenAncestor = function(bHiddenAncestor, animator)
{
  var oldHiddenAncestor = this._bHiddenAncestor;
  this._bHiddenAncestor = bHiddenAncestor;
  if (bHiddenAncestor != oldHiddenAncestor) {
    //Bug 13381726: transition when HiddenAncestor is set, don't wait
    //until next render, and don't set bChanged flag
    //this._bHiddenAncestorChanged = true;
    this._refreshCursor();
    this.TransitionHiddenAncestor(animator);

    //Bug 13382113: need to transition the overview node at the same time
    this.GetDiagram().UpdateOverviewNode(this, animator);
  }
};

/**
 * Force set Zoom 100 Facet in the Node
 * @param {boolean} bForceZoom100  true to force set zoom100 facet
 */
dvt.AdfDiagramNode.prototype.setForceZoom100Facet = function(bForceZoom100)
{
  this._bForceZoom100Facet = bForceZoom100;
};


/**
 * Gets the node template based on the current zoom
 *
 * @return {dvt.AfComponent} the current node template
 */
dvt.AdfDiagramNode.prototype.GetNodeTemplate = function()
{
  var diagram = this.GetDiagram();
  if (!diagram) {
    return null;
  }
  var zoomFacet;
  var nodeTemplate = null;
  var oldZoom = this._zoom;
  if (this._bForceZoom100Facet) {
    zoomFacet = 'zoom100';
    this._zoom = 1;
    nodeTemplate = this.getNodeDef().getNodeTemplate(zoomFacet);
    if (!nodeTemplate) {
      nodeTemplate = this.getNodeDef().getDefaultNodeTemplate(this.getWidth(), this.getHeight());
    }
  }
  else {
    var panZoomCanvas = diagram.getPanZoomCanvas();
    if (!panZoomCanvas) {
      return null;
    }
    var zoom = panZoomCanvas.getZoom();
    //find the first zoom facet for the given zoom that's been specified
    if (zoom <= .25 && !nodeTemplate) {
      zoomFacet = 'zoom25';
      this._zoom = .25;
      nodeTemplate = this.getNodeDef().getNodeTemplate(zoomFacet);
    }
    if (zoom <= .50 && !nodeTemplate) {
      zoomFacet = 'zoom50';
      this._zoom = .5;
      nodeTemplate = this.getNodeDef().getNodeTemplate(zoomFacet);
    }
    if (zoom <= .75 && !nodeTemplate) {
      zoomFacet = 'zoom75';
      this._zoom = .75;
      nodeTemplate = this.getNodeDef().getNodeTemplate(zoomFacet);
    }
    if (!nodeTemplate) {
      zoomFacet = 'zoom100';
      this._zoom = 1;
      nodeTemplate = this.getNodeDef().getNodeTemplate(zoomFacet);
      if (!nodeTemplate) {
        nodeTemplate = this.getNodeDef().getDefaultNodeTemplate(this.getWidth(), this.getHeight());
      }
    }
  }

  //if the view zoomed across a threshold, and if the template didn't
  //change, then return null
  //OR if the zoom threshold hasn't changed and we've already rendered
  //Bug 16689345 - HTML5 HV: WRONG ZOOM FACET AFTER LATERAL NAVIGATION
  //Bug 16689332 - HTML5 HV: NEWLY FETCHED NODE SHOWS WRONG ZOOM FACET
  //Bug 16953938 - SIZE OF THE DIAGRAM NODE UNDERLAY IS WRONG
  //if force Zoom100 flag is set, don't skip next renderer
  if (oldZoom == this._zoom && !this._bForceZoom100Facet &&
      ((diagram._bCrossedZoomThreshold && !this._bHiddenAncestorChanged) ||
       (!diagram._bCrossedZoomThreshold && this._afRoot && ! diagram._bAfImageLoaded)))
  {
    nodeTemplate = null;
  }

  return nodeTemplate;
};

dvt.AdfDiagramNode.prototype.RemoveOldNodeContents = function(animator)
{
  if (this._afRoot) {
    this._getContentContainer().removeChild(this._afRoot);
    if (!(animator && this._bDisclosureChanged && this.isDisclosed())) {
      if (this._afRoot.destroy) {
        this._afRoot.destroy();
      }
    }
    this._afRoot = null;
  }

  //save old label bounds so that we can apply them to a new label
  this._oldLabelBounds = null;
  this._oldLabelPos = null;
  this._oldLabelRotAngle = null;
  this._oldLabelRotPoint = null;
  if (this._labelObj)
  {
    this._oldLabelBounds = this.getLabelBounds();
    this._oldLabelPos = this.getLabelPosition();
    this._oldLabelRotAngle = this.getLabelRotationAngle();
    this._oldLabelRotPoint = this.getLabelRotationPoint();
    this._getContentContainer().removeChild(this._labelObj);
    if (this._labelObj.destroy)
    {
      this._labelObj.destroy();
    }
    this._labelObj = null;
  }

  if (this._backgroundObj) {
    this._getContentContainer().removeChild(this._backgroundObj);
    if (this._backgroundObj.destroy) {
      this._backgroundObj.destroy();
    }
    this._backgroundObj = null;
  }

  if (this._backgroundMouseEventCatcher) {
    this._getContentContainer().removeChild(this._backgroundMouseEventCatcher);
    if (this._backgroundMouseEventCatcher.destroy) {
      this._backgroundMouseEventCatcher.destroy();
    }
    this._backgroundMouseEventCatcher = null;
  }

  if (this._overlays) {
    for (var iOv = 0; iOv < this._overlays.length; iOv++)
    {
      var overlay = this._overlays[iOv]['overlay'];
      this._getContentContainer().removeChild(overlay);
      if (overlay.destroy)
      {
        overlay.destroy();
      }
    }
    this._overlays = null;
  }

  if (this._containerTopObj) {
    if (!(animator && this._bDisclosureChanged && !this.isDisclosed())) {
      var parent = this._containerTopObj.getParent();
      if (parent) {
        parent.removeChild(this._containerTopObj);
      }
      if (this._containerTopObj.destroy) {
        this._containerTopObj.destroy();
      }
    }
    this._containerTopObj = null;
  }

  if (this._containerBottomObj) {
    if (!(animator && this._bDisclosureChanged && !this.isDisclosed())) {
      var parent = this._containerBottomObj.getParent();
      if (parent) {
        parent.removeChild(this._containerBottomObj);
      }
      if (this._containerBottomObj.destroy) {
        this._containerBottomObj.destroy();
      }
    }
    this._containerBottomObj = null;
  }

  if (this._containerLeftObj) {
    if (!(animator && this._bDisclosureChanged && !this.isDisclosed())) {
      var parent = this._containerLeftObj.getParent();
      if (parent) {
        parent.removeChild(this._containerLeftObj);
      }
      if (this._containerLeftObj.destroy) {
        this._containerLeftObj.destroy();
      }
    }
    this._containerLeftObj = null;
  }

  if (this._containerRightObj) {
    if (!(animator && this._bDisclosureChanged && !this.isDisclosed())) {
      var parent = this._containerRightObj.getParent();
      if (parent) {
        parent.removeChild(this._containerRightObj);
      }
      if (this._containerRightObj.destroy) {
        this._containerRightObj.destroy();
      }
    }
    this._containerRightObj = null;
  }

  if (this._containerChrome) {
    this._getContentContainer().removeChild(this._containerChrome);
    if (!(animator && this._bDisclosureChanged && !this.isDisclosed())) {
      if (this._containerChrome.destroy) {
        this._containerChrome.destroy();
      }
    }
    this._containerChrome = null;
  }

  if (this._containerUnderlay) {
    this._getContentContainer().removeChild(this._containerUnderlay);
    if (!(animator && this._bDisclosureChanged && this.isDisclosed())) {
      if (this._containerUnderlay.destroy) {
        this._containerUnderlay.destroy();
      }
    }
    this._containerUnderlay = null;
  }

  if (this._focusUnderlay) {
    this._getContentContainer().removeChild(this._focusUnderlay);
    if (this._focusUnderlay.destroy) {
      this._focusUnderlay.destroy();
    }
    this._focusUnderlay = null;
  }

  //  if (this._childNodePane) {
  //    this.removeChild(this._childNodePane);
  //  }
  this._hover = false;
};

dvt.AdfDiagramNode.prototype.RemoveDispObj = function(dispObj) {
  if (dispObj) {
    var parent = dispObj.getParent();
    if (parent) {
      parent.removeChild(dispObj);
    }
    if (dispObj.destroy) {
      dispObj.destroy();
    }
  }
};

dvt.AdfDiagramNode.prototype.DestroyDisclosedContent = function(owner) {
  var parent;

  if (owner._containerTopObj) {
    parent = owner._containerTopObj.getParent();
    if (parent) {
      parent.removeChild(owner._containerTopObj);
    }
    if (owner._containerTopObj.destroy) {
      owner._containerTopObj.destroy();
    }
    owner._containerTopObj = null;
  }

  if (owner._containerBottomObj) {
    parent = owner._containerBottomObj.getParent();
    if (parent) {
      parent.removeChild(owner._containerBottomObj);
    }
    if (owner._containerBottomObj.destroy) {
      owner._containerBottomObj.destroy();
    }
    owner._containerBottomObj = null;
  }

  if (owner._containerLeftObj) {
    parent = owner._containerLeftObj.getParent();
    if (parent) {
      parent.removeChild(owner._containerLeftObj);
    }
    if (owner._containerLeftObj.destroy) {
      owner._containerLeftObj.destroy();
    }
    owner._containerLeftObj = null;
  }

  if (owner._containerRightObj) {
    parent = owner._containerRightObj.getParent();
    if (parent) {
      parent.removeChild(owner._containerRightObj);
    }
    if (owner._containerRightObj.destroy) {
      owner._containerRightObj.destroy();
    }
    owner._containerRightObj = null;
  }

  if (owner._containerChrome) {
    parent = owner._containerChrome.getParent();
    if (parent) {
      parent.removeChild(owner._containerChrome);
    }
    if (owner._containerChrome.destroy) {
      owner._containerChrome.destroy();
    }
    owner._containerChrome = null;
  }
};

dvt.AdfDiagramNode.prototype.DestroyUndisclosedContent = function(owner) {
  var parent;
  if (owner._afRoot) {
    parent = owner._afRoot.getParent();
    if (parent) {
      parent.removeChild(owner._afRoot);
    }
    if (owner._afRoot.destroy) {
      owner._afRoot.destroy();
    }
    owner._afRoot = null;
  }

  if (owner._containerUnderlay) {
    parent = owner._containerUnderlay.getParent();
    if (parent) {
      parent.removeChild(owner._containerUnderlay);
    }
    if (owner._containerUnderlay.destroy) {
      owner._containerUnderlay.destroy();
    }
    owner._containerUnderlay = null;
  }
};


/**
 * Creates node content - parse, stamp af components and connect them to the parent
 * but not sizing or layout here
 * @param {dvt.AfContext} afContext AfContext for the node
 * @param {dvt.Animator} animator Optional animator for the node content
 */
dvt.AdfDiagramNode.prototype.createNodeContents = function(afContext, animator)
{
  var bDisclosed = this.isDisclosed();// && this.isContainer();
  var nodeTemplate = this.GetNodeTemplate();
  if ((!nodeTemplate && !bDisclosed) ||
      (bDisclosed && this.GetDiagram()._bCrossedZoomThreshold && this._containerChrome))
  {
    //if there's no node template, then we crossed a zoom threshold
    //for which a facet isn't specified, so skip the next render
    this._bIgnoreNextRender = true;
    return;
  }

  //    if (this.isHiddenAncestor() && !this._bHiddenAncestorChanged) {
  //      return;
  //    }

  if (animator) {
    this._savedContentState = this.SaveContentState();
  }

  if (!this._copyForDelayedRemoval && !this._canRemoveNodeContent())
    this._copyForDelayedRemoval = this.CreateCopyForDelayedRemoval();
  else
    this.RemoveOldNodeContents(animator);
  afContext.setELContext(this.getData().getElAttributes());

  //set keyboard focusable objects
  this._tabStopsNodeArray = [];
  afContext.setTabStopsArray(this._tabStopsNodeArray);
  afContext.registerTabStop(this);
  this._curTabStopIdx = 0;


  // add action listener to the command objects in the node
  afContext.setContextCallback(this, this.eventContextCallback);

  // add panelCard event listener
  var diagram = this.GetDiagram();
  if (diagram.panelCardEventCallback)
    afContext.setPanelCardCallback(diagram, diagram.panelCardEventCallback);

  //we dont need avaiable width and height
  if (!bDisclosed)
  {
    //this._afRoot = dvt.AfComponentFactory.parseAndStamp(
    //  afContext, nodeTemplate, this._contentPane);
    this._afRoot = dvt.AfComponentFactory.parseAndStamp(
        afContext, nodeTemplate, this);
  }

  if (this.HasLabel())
  {
    this._labelObj = this.CreateLabel(afContext);
    if (this._labelObj)
    {
      this._getContentContainer().addChildAt(this._labelObj, 0);
      //this.GetDiagram().getEventManager().associate(this._labelObj, this);
    }
    //initially apply old label bounds so that later layout transition
    //looks more natural
    if (this._oldLabelBounds)
    {
      this.setLabelBounds(this._oldLabelBounds);
      this._oldLabelBounds = null;
    }
    if (this._oldLabelPos)
    {
      this.setLabelPosition(this._oldLabelPos);
      this._oldLabelPos = null;
    }
    if (this._oldLabelRotAngle != null)
    {
      this.setLabelRotationAngle(this._oldLabelRotAngle);
      this._oldLabelRotAngle = null;
    }
    if (this._oldLabelRotPoint)
    {
      this.setLabelRotationPoint(this._oldLabelRotPoint);
      this._oldLabelRotPoint = null;
    }
  }
  /*
  if (this._nodeDef.getLabelTemplate())
  {
    this._labelObj = dvt.AfComponentFactory.parseAndStamp(
      afContext, this._nodeDef.getLabelTemplate(), this);
    if (this._labelObj)
    {
      this._getContentContainer().addChildAt(this._labelObj, 0);
      //this.GetDiagram().getEventManager().associate(this._labelObj, this);
    }
    //initially apply old label bounds so that later layout transition
    //looks more natural
    if (this._oldLabelBounds)
    {
      this.setLabelBounds(this._oldLabelBounds);
      this._oldLabelBounds = null;
    }
  }
  */
  if (!bDisclosed) {
    // Do not display overlays for expanded containers
    var overlayTemplate = this._nodeDef.getOverlayTemplate();
    if (overlayTemplate)
    {
      var arPointLocations = [];
      if (overlayTemplate instanceof dvt.AfGroup) {
        var group = overlayTemplate.stamp(afContext.getELContext());
        if (group.isRendered()) {
          // Stamped group does not contain children; get the unstamped versions from the template
          for (var i = 0; i < overlayTemplate.getNumChildren(); i++) {
            var child = overlayTemplate.getChildAt(i);
            if (child instanceof dvt.AfPointLocation) {
              arPointLocations.push(child);
            }
          }
        }
      }
      else if (overlayTemplate instanceof dvt.AfPointLocation) {
        arPointLocations.push(overlayTemplate);
      }
      var numOverlays = arPointLocations.length;
      this._overlays = [];
      //loop in reverse order because each overlay is added
      //at child index 0
      for (var iOverlay = numOverlays - 1; iOverlay >= 0; iOverlay--) {
        // stamp out the point location to resolve pointX,pointY
        var pointLocation = arPointLocations[iOverlay].stamp(afContext.getELContext());
        var pointX = 0;
        var pointY = 0;
        try {
          pointX = parseFloat(pointLocation.getProperty(dvt.AdfDiagramParser._ATTR_POINT_X)) / 100;
          pointY = parseFloat(pointLocation.getProperty(dvt.AdfDiagramParser._ATTR_POINT_Y)) / 100;
        }
        catch (pointE) {
        }
        if (!pointX) {
          pointX = 0;
        }
        if (!pointY) {
          pointY = 0;
        }

        // Now stamp out the actual overlay marker
        //Bug 14456277: only need to call parseAndStamp here, not parseAndLayout
        var overlay = dvt.AfComponentFactory.parseAndStamp(afContext, arPointLocations[iOverlay].getChildAt(0), this);
        if (overlay) {
          this._overlays.push({'overlay': overlay, 'position': new dvt.Point(pointX, pointY)});
          this._getContentContainer().addChildAt(overlay, 0);
          //this.GetDiagram().getEventManager().associate(overlay, this);
        }
      }
    }
  }

  //add child node pane underneath any overlays
  if (this.isDisclosed() && this._childNodePane) {
    this.addChild(this._childNodePane);
  }

  if (this._afRoot)
  {
    //add AF content of this node underneath any child nodes
    this._getContentContainer().addChildAt(this._afRoot, 0);
  }
  else if (animator && this._bDisclosureChanged && this.isDisclosed()) {
    // add a placeholder so that we know where to add the old afRoot before we fade it out
    this._afRootPlaceholder = new dvt.Container(this.getCtx(), this.getData().getId() + '_afrp');
    this._getContentContainer().addChildAt(this._afRootPlaceholder, 0);
  }

  if (bDisclosed) {
    this._containerChrome = this.CreateContainerChrome();
    this._getContentContainer().addChildAt(this._containerChrome, 0);

    //add container top to container chrome
    if (this._nodeDef.getContainerTopTemplate()) {
      this._addContainerFacetTabStops(afContext, 'top');
      this._containerTopObj = dvt.AfComponentFactory.parseAndStamp(afContext, this._nodeDef.getContainerTopTemplate(), this);
      if (this._containerTopObj) {
        this._containerChrome.addChild(this._containerTopObj);
      }
    }
    //add container bottom to container chrome
    if (this._nodeDef.getContainerBottomTemplate()) {
      this._addContainerFacetTabStops(afContext, 'bottom');
      this._containerBottomObj = dvt.AfComponentFactory.parseAndStamp(afContext, this._nodeDef.getContainerBottomTemplate(), this);
      if (this._containerBottomObj) {
        this._containerChrome.addChild(this._containerBottomObj);
      }
    }
    //add container left to container chrome
    if (this._nodeDef.getContainerLeftTemplate()) {
      this._addContainerFacetTabStops(afContext, 'left');
      this._containerLeftObj = dvt.AfComponentFactory.parseAndStamp(afContext, this._nodeDef.getContainerLeftTemplate(), this);
      if (this._containerLeftObj) {
        this._containerChrome.addChild(this._containerLeftObj);
      }
    }
    //add container right to container chrome
    if (this._nodeDef.getContainerRightTemplate()) {
      this._addContainerFacetTabStops(afContext, 'right');
      this._containerRightObj = dvt.AfComponentFactory.parseAndStamp(afContext, this._nodeDef.getContainerRightTemplate(), this);
      if (this._containerRightObj) {
        this._containerChrome.addChild(this._containerRightObj);
      }
    }
    this._updateNodeTabStops();
  }

  //register itself again to have the node as the last tabstop as well
  // do this before the tab stops array is reset in background template in the code below
  if (this._tabStopsNodeArray.length > 1)
    this._tabStopsNodeArray.push(this);


  // add background under main node content
  if (this._nodeDef.getBackgroundTemplate()) {
    afContext.setTabStopsArray(null); //do not register background as tabstop
    this._backgroundObj = dvt.AfComponentFactory.parseAndStamp(afContext, this._nodeDef.getBackgroundTemplate(), this);
    if (this._backgroundObj) {
      this._backgroundObj.setMouseEnabled(false);
      var backgroundContainer = bDisclosed ? this._containerChrome : this._getContentContainer();
      backgroundContainer.addChildAt(this._backgroundObj, 0);
      //this.GetDiagram().getEventManager().associate(this._backgroundObj, this);

      this._backgroundMouseEventCatcher = new dvt.Rect(this.getCtx(), 0, 0, 0, 0);
      this._backgroundMouseEventCatcher.setInvisibleFill();
      this._getContentContainer().addChildAt(this._backgroundMouseEventCatcher, 1);
    }
  }


  //make sure stack underlay stays under content
  if (this._stackUnderlay)
  {
    this._getContentContainer().addChildAt(this._stackUnderlay, 0);
  }
  //this.GetDiagram().getEventManager().associate(this._afRoot, this);

  //TODO: re-enable mouse interaction on children
  //HZHANG: I removed DvtIterator because it was causing unnecessary code bloat.
  /*var children = this._afRoot.getIterator();
  var child = children.getNext();
  while (child)
  {
    child.setMouseEnabled(false);
    child = children.getNext();
  }*/
};

dvt.AdfDiagramNode.prototype.CreateContainerChrome = function()
{
  return new dvt.Rect(this.getCtx(), 0, 0, 1, 1);
};


/**
 * Renders the container chrome that will be displayed when this node is disclosed
 */
dvt.AdfDiagramNode.prototype.RenderContainerChrome = function()
{
  this._containerChrome.setWidth(this._nodeWidth);
  this._containerChrome.setHeight(this._nodeHeight);
  this._containerChrome.setRx(this._getContainerBorderRx());
  this._containerChrome.setRy(this._getContainerBorderRx());
  this._containerChrome.setSolidFill(this._getContainerBgColor());
  this._containerChrome.setSolidStroke(this._getContainerBorderColor(), 1, this._getContainerBorderWidth());
  this._setContainerChromeClipPath(this._getContainerChromeClipPathDef());
};

dvt.AdfDiagramNode.prototype._setContainerChromeClipPath = function(clipPathDef) {

  var clipId = 'ctClip_' + dvt.AfComponent.genUniqueId();
  var clipPath = new dvt.ClipPath(clipId);
  var x = clipPathDef[dvt.AdfDiagramNode._CONTAINER_CHROME_CLIP_PATH_X];
  var y = clipPathDef[dvt.AdfDiagramNode._CONTAINER_CHROME_CLIP_PATH_Y];
  var w = clipPathDef[dvt.AdfDiagramNode._CONTAINER_CHROME_CLIP_PATH_W];
  var h = clipPathDef[dvt.AdfDiagramNode._CONTAINER_CHROME_CLIP_PATH_H];
  var rx = clipPathDef[dvt.AdfDiagramNode._CONTAINER_CHROME_CLIP_PATH_RX];
  var ry = clipPathDef[dvt.AdfDiagramNode._CONTAINER_CHROME_CLIP_PATH_RY];
  clipPath.addRect(x, y, w, h, rx, ry);
  this._containerChrome.setClipPath(clipPath);
};

dvt.AdfDiagramNode.prototype._getContainerChromeClipPathDef = function() {
  var chromeDims = this._containerChrome.getDimensions();
  var clipPathDef = new Array();
  clipPathDef[dvt.AdfDiagramNode._CONTAINER_CHROME_CLIP_PATH_X] = chromeDims.x;
  clipPathDef[dvt.AdfDiagramNode._CONTAINER_CHROME_CLIP_PATH_Y] = chromeDims.y;
  clipPathDef[dvt.AdfDiagramNode._CONTAINER_CHROME_CLIP_PATH_W] = chromeDims.w;
  clipPathDef[dvt.AdfDiagramNode._CONTAINER_CHROME_CLIP_PATH_H] = chromeDims.h;
  clipPathDef[dvt.AdfDiagramNode._CONTAINER_CHROME_CLIP_PATH_RX] = this._containerChrome.getRx();
  clipPathDef[dvt.AdfDiagramNode._CONTAINER_CHROME_CLIP_PATH_RY] = this._containerChrome.getRy();

  return clipPathDef;
};

dvt.AdfDiagramNode.prototype.createDetailContents = function(afContext)
{
  if (this._nodeDef)
  {
    if (!this.getNodeDef().getDetailTemplate())
    {
      return;
    }

    afContext.setELContext(this.getData().getElAttributes());
    // add action listener to the command objects in the node
    afContext.setContextCallback(this, this.contextCallback);

    this._detailContents = dvt.AfComponentFactory.parseAndStamp(
        afContext, this.getNodeDef().getDetailTemplate(),
        this.GetDiagram().getPanZoomCanvas().getContentPane());

    return this._detailContents;
  }
  return null;
};


/**
 * Renders the node detail contents (for the hover window)
 *
 * @param {dvt.AfContext} afContext the af component rendering context
 */
dvt.AdfDiagramNode.prototype.renderDetailContents = function(afContext)
{
  if (this._nodeDef && this._detailContents)
  {
    var availWidth = this.getWidth();
    var availHeight = this.getHeight();

    afContext.setAvailableWidth(availWidth);
    afContext.setAvailableHeight(availHeight);

    dvt.AfComponentFactory.layoutAndRender(afContext, this._detailContents);
  }
};


/**
 * @override
 */
dvt.AdfDiagramNode.prototype.isSelectable = function() {
  //Bug 15838130: disallow selection on drill ancestor
  if (this.isHiddenAncestor()) {
    return false;
  }
  return dvt.AdfDiagramNode.superclass.isSelectable.call(this);
};


/**
 * @override
 */
dvt.AdfDiagramNode.prototype.setSelected = function(selected) {
  dvt.AdfDiagramNode.superclass.setSelected.call(this, selected);
  if (this.isHiddenAncestor())
    return;
  this.RefreshSelectionEffect();
  this.UpdateAccessibilityAttributes();
  //link handle behavior is tied up with selection events on touch devices and with hover events on desktop
  if (dvt.Agent.isTouchDevice() && selected)
    this.showLinkHandle();
  else if (dvt.Agent.isTouchDevice())
    this.hideLinkHandle();
};


/**
 * Gets whether this node has focus or not
 * @return {boolean} whether this node has focus or not
 */
dvt.AdfDiagramNode.prototype.isFocused = function() {
  return this._bFocused;
};


/**
 * Sets whether this node has focus or not
 * @param {boolean} bFocused whether this node has focus or not
 */
dvt.AdfDiagramNode.prototype.setFocused = function(bFocused) {
  // Store the focus state
  this._bFocused = bFocused;
};

dvt.AdfDiagramNode.prototype.setLabel = function(label)
{
  this._label = label;
};

/**
 * Get the diagram node label
 * @return {String} Diagram node label
 */
dvt.AdfDiagramNode.prototype.getLabel = function()
{
  return this._label;
};



dvt.AdfDiagramNode.prototype.setDeleteEnabled = function(enabled)
{
  this._deleteEnabled = enabled;
};

dvt.AdfDiagramNode.prototype.isDeleteEnabled = function()
{
  return this._deleteEnabled;
};

dvt.AdfDiagramNode.prototype.RefreshSelectionEffect = function() {
  if (this._bHighlight) {
    this._hideFeedback();
  }

  // if vector effects are not supported, update the stroke widths based on current zoom level before highlighting
  if (!this._diagram.supportsVectorEffects()) {
    this.UpdateHighlightStrokes();
  }

  // We usually only update the hover effect via the selection handler, which already checks whether we're selectable.
  // RefreshSelectionEffect happens outside of this mechanism, so we need to make the check explicitly
  var hover = this.isSelectable() && this._hover;
  if (this.getVisible() && (hover || this._bFocusEffect || this._selected)) {
    this._showFeedback(hover || this._bFocusEffect, this._selected);
  }
};


/**
 * Adds the node shadow if one has been specified
 */
dvt.AdfDiagramNode.prototype.AddDrawEffects = function() {

  //Bug 17185012, 17185275: remove shadow for FF and Safari on iOS
  if (dvt.Agent.isPlatformGecko() || (dvt.Agent.isTouchDevice() && dvt.Agent.isBrowserSafari()))
    return;

  if (this._shadow == null) {
    var style = this._diagram.getNodeStyles()[dvt.AdfDiagramNode.CSS_STYLE];
    if (style)
      this._shadow = this._diagram.CreateDropShadowFilter(style.getStyle(dvt.PanZoomComponent.BOX_SHADOW));
    if (this._shadow)
      this.addDrawEffect(this._shadow);
  }

};


/**
 * Set the stack status of the node
 * @param {boolean} bStack  stack status of the node
 */
dvt.AdfDiagramNode.prototype.setStack = function(bStack) {
  //Store the state of stack flag before changing it
  if (this._bStack != bStack)
    this.GetDiagram().setAutomationState(this.getData().getId(), 'stack', this._bStack);

  this._bStack = bStack;

  if (bStack)
  {
    if (this._stackBadge)
    {
      this._getContentContainer().removeChild(this._stackBadge);
      this._stackBadge = null;
      this._stackBadgeHandler = null;
    }
    this.CreateStackBadge();
    if (this._stackUnderlay)
    {
      this._getContentContainer().removeChild(this._stackUnderlay);
    }
    this.CreateStackUnderlay();
  }
  else
  {
    if (this._stackBadge)
    {
      this._getContentContainer().removeChild(this._stackBadge);
      this._stackBadge = null;
    }
    if (this._stackUnderlay)
    {
      this._getContentContainer().removeChild(this._stackUnderlay);
      this._stackUnderlay = null;
    }
  }
};


/**
 * Check if the Diagram node is stacked
 * @return {boolean}  true if the diagram node is stacked
 */
dvt.AdfDiagramNode.prototype.isStack = function() {
  return this._bStack;
};

dvt.AdfDiagramNode.prototype.GetStackId = function() {
  var stackId;
  var nodeStackMap = this._diagram.GetNodeStackMap();
  if (nodeStackMap) {
    stackId = nodeStackMap[this.getData().getId()];
  }
  return stackId;
};


/**
 *
 */
dvt.AdfDiagramNode.prototype.setStackCount = function(count) {
  this._stackCount = count;
};


/**
 *
 */
dvt.AdfDiagramNode.prototype.getStackCount = function() {
  return this._stackCount;
};


/**
 * Creates the stack count indicator
 */
dvt.AdfDiagramNode.prototype.CreateStackBadge = function() {
  var stackCount;
  //TO DO: use resource for pattern?
  if (this.getStackCount() <= 1000) {
    stackCount = '' + this.getStackCount();
  }
  else {
    stackCount = '1000+';
  }
  var text = new dvt.OutputText(this.getCtx(), stackCount, 0, 0);
  this.applyBadgeTextStyles(text);
  this._getContentContainer().addChild(text);
  var dims;
  //use the saved node size, if available, so that the stack badge
  //position is not affected by other node overlays
  if (this._nodeWidth && this._nodeHeight)
    dims = new dvt.Rectangle(0, 0, this._nodeWidth, this._nodeHeight);
  else
    dims = this.getDimensionsWithStroke();

  var textDims = text.measureDimensions();

  var hpad = 3;
  var vpad = 1;//4;  //supposed to be 4, but it already looks like there are 3 built in
  var rectW = textDims.w + 2 * hpad;
  var rectH = textDims.h + 2 * vpad;
  var rect = new dvt.Rect(this.getCtx(), 0, 0, rectW, rectH);

  text.setTranslate(hpad, vpad);
  rect.setTranslate(dims.w + dvt.AdfDiagramNode._STACK_UNDERLAY_PADDING - rectW - dvt.AdfDiagramNode._STACK_BADGE_OFFSET,
                    -dvt.AdfDiagramNode._STACK_UNDERLAY_PADDING + dvt.AdfDiagramNode._STACK_BADGE_OFFSET);

  this.applyBadgeStyles(rect);
  rect.addChild(text);
  this._getContentContainer().addChild(rect);

  this._stackBadge = rect;

  this._stackBadgeHandler = new DvtAdfDiagramStackBadgeHandler(this.GetDiagram(), this);
  this.GetDiagram().getEventManager().associate(this._stackBadge, this._stackBadgeHandler);
};


/**
 * Creates the underlay that is displayed when the node is in a stack
 *
 * @protected
 */
dvt.AdfDiagramNode.prototype.CreateStackUnderlay = function() {
  if (this._afRoot)
  {
    var padding = dvt.AdfDiagramNode._STACK_UNDERLAY_PADDING;
    var strokeWidth = 1;

    var xx = -padding;
    var yy = -padding;
    var ww = this._nodeWidth + 2 * padding;
    var hh = this._nodeHeight + 2 * padding;

    var shape1 = new dvt.Rect(this.getCtx(), xx, yy, ww, hh);
    var shape2 = new dvt.Rect(this.getCtx(), xx, yy, ww, hh);
    var shape3 = new dvt.Rect(this.getCtx(), xx, yy, ww, hh);

    var underlayStyles = this._diagram.getStackUnderlayStyles();
    if (underlayStyles) {
      var stackUnderlayFill = underlayStyles.getStyle(dvt.CSSStyle.BACKGROUND_COLOR);
      var stackUnderlayStroke = underlayStyles.getStyle(dvt.CSSStyle.BORDER_COLOR);
      var stackUnderlayRadius = underlayStyles.getStyle(dvt.CSSStyle.BORDER_RADIUS);

      var fill = new dvt.SolidFill(stackUnderlayFill, 1);
      var stroke = this._getUnderlayStroke(stackUnderlayStroke, 1, strokeWidth);
      var radius = stackUnderlayRadius ? dvt.CSSStyle.toNumber(stackUnderlayRadius) : 0;

      shape1.setFill(fill);
      shape2.setFill(fill);
      shape3.setFill(fill);
      shape1.setStroke(stroke);
      shape2.setStroke(stroke);
      shape3.setStroke(stroke);
      if (radius) {
        shape1.setRx(radius);
        shape1.setRy(radius);
        shape2.setRx(radius);
        shape2.setRy(radius);
        shape3.setRx(radius);
        shape3.setRy(radius);
      }
      shape1.setPixelHinting(true);
      shape2.setPixelHinting(true);
      shape3.setPixelHinting(true);
    }

    shape2.setTranslate(dvt.AdfDiagramNode._STACK_UNDERLAY_OFFSET, -dvt.AdfDiagramNode._STACK_UNDERLAY_OFFSET);

    shape3.setTranslate(dvt.AdfDiagramNode._STACK_UNDERLAY_OFFSET * 2, -dvt.AdfDiagramNode._STACK_UNDERLAY_OFFSET * 2);

    var underlay = new dvt.Container(this.getCtx());
    underlay.addChild(shape3);
    underlay.addChild(shape2);
    underlay.addChild(shape1);

    this._getContentContainer().addChildAt(underlay, 0);

    this._stackUnderlay = underlay;
  }
};

/**
 * @override
 */
dvt.AdfDiagramNode.prototype.getCategories = function() {
  return this.getData().getAttributeGroups();
};

dvt.AdfDiagramNode.prototype.getDisplayables = function() {
  // This function is only used to determine what to alpha fade for highlighting
  var ar = [];
  ar.push(this._getContentContainer());
  return ar;
};

/**
 * @protected
 * Inserts node into a parent container
 * @param {dvt.Container} parent parent container
 */
dvt.AdfDiagramNode.prototype.InsertNodeIntoParent = function(parent) {
  if (!parent)
    return;
  //Bug 16036538: if this node is already a child of the parent, remove it first so that
  //the insert index calculated below is correct (avoids edge case where node is inserted
  //into list > its current index, in which case the insert index ends up being off by 1
  //because the node has to be removed before it can be re-added, so that all of the entries
  //after it have been shifted down by 1)
  if (this.getParent() == parent) {
    parent.removeChild(this);
  }

  var preferredInsertLocation = this.GetPreferredInsertLocation(parent);
  if (preferredInsertLocation != -1) {
    parent.addChildAt(this, preferredInsertLocation);
  }
  else {
    parent.addChild(this);
  }
};

dvt.AdfDiagramNode.prototype.GetPreferredInsertLocation = function(parent) {
  var numChildren = parent.getNumChildren();
  for (var i = numChildren - 1; i >= 0; i--) {
    var child = parent.getChildAt(i);
    if (child instanceof dvt.AdfDiagramNode) {
      //Bug 16036538: if changing the z-order when a node is highlighted, insert the node to sort
      //the z-order such that:
      //*) the node showing hover effect is at the front
      //*) selected nodes not showing the hover effect are behind the hover node
      //*) all unselected, unhovered nodes are behind the selected nodes and hover node
      if (!dvt.AdfDiagramNode._CHANGE_Z_ORDER_ON_HIGHLIGHT ||
          (this != child && (this._bHoverEffect || (this.isSelected() && !child._bHoverEffect) ||
                             (!this._bHoverEffect & !this.isSelected() && !child._bHoverEffect && !child.isSelected())))) {
        return i + 1;
      }
    }
    else if (child instanceof dvt.AdfDiagramLink) {
      if (!child._bCrossContainerLink) {
        return i + 1;
      }
    }
  }
  return -1;
};


/**
 * @protected
 */
dvt.AdfDiagramNode.prototype.GetParentContainer = function() {
  var parent = null;
  var diagram = this.GetDiagram();
  if (diagram) {
    parent = this._nodeParent;
    if (!parent) {
      parent = diagram._nodesPane;
    }
  }
  return parent;
};

/**
 * Sets visibility of the node.
 * @param {boolean} bVisible True to make the node visible
 */
dvt.AdfDiagramNode.prototype.setVisible = function(bVisible) {
  //Store the visible state before changing it
  if (this._bVisible != bVisible)
    this.GetDiagram().setAutomationState(this.getData().getId(), 'visible', this._bVisible);
  dvt.AdfDiagramNode.superclass.setVisible.call(this, bVisible);
  var diagram = this.GetDiagram();
  if (diagram) {
    var parent = this.GetParentContainer();
    var contained = parent.contains(this);
    if (bVisible && !contained) {
      this.InsertNodeIntoParent(parent);

      this.FullRenderNode();

      this._cachedDiagram = null;
    }
    else if (!bVisible && contained) {
      parent.removeChild(this);
      this._cachedDiagram = diagram;
    }
  }
  DvtAdfDiagramOverviewUtils.SetNodeVisible(diagram, this, bVisible);
};


/**
 * Render Node
 * @param {boolean} bForce Whether rerender should be forced (e.g. even when rerendering at the same zoom level)
 * @protected
 */
dvt.AdfDiagramNode.prototype.FullRenderNode = function(bForce) {
  var afContext = this.GetDiagram().createAfContext(this.getData().getId());
  if (bForce) {
    this._zoom = -1;
  }
  this.createNodeContents(afContext);

  this.renderNode(afContext);
};


/**
 * Render container facet
 * @param {dvt.AfPanelGroupLayout} template  render template
 * @param {string} facetName facet name
 * @param {number} availWidth  Available Width
 * @param {number} availHeight  Available Height
 * @param {number} minWidth min width for the facet
 * @param {number} minHeight min height for the facet
 * @return {object} object contains facet object and bounds
 */
dvt.AdfDiagramNode.prototype.FullRenderContainerFacet = function(template, facetName, availWidth, availHeight, minWidth, minHeight) {
  if (!template) {
    return null;
  }

  var afContext = this.GetDiagram().createAfContext(this.getData().getId());
  // add action listener to the command objects in the node
  afContext.setContextCallback(this, this.eventContextCallback);
  this._addContainerFacetTabStops(afContext, facetName);

  var facetObj = dvt.AfComponentFactory.parseAndStamp(afContext, template, this);
  var facetAvailBounds = null;
  var facetBounds = null;
  if (facetObj && this._containerChrome) {
    this._containerChrome.addChild(facetObj);

    afContext.setAvailableWidth(availWidth);
    afContext.setAvailableHeight(availHeight);
    //need to save the available bounds given to the facet to
    //workaround bug 13878434
    facetAvailBounds = {w: availWidth, h: availHeight};

    dvt.AfComponentFactory.layoutAndRender(afContext, facetObj);

    //Bug 14374448 - DIAGRAMMER: <AF:OUTPUTFORMATTED> SIZES ITS CONTAINING LAYOUT INCONSISTENTLY
    // facetBounds = facetObj.getDimensions();
    facetBounds = dvt.AfComponentFactory.getBounds(facetObj);

    if (minWidth && facetBounds.w < minWidth) {
      afContext.setAvailableWidth(minWidth);
      facetAvailBounds = {w: minWidth, h: availHeight};
      dvt.AfComponentFactory.layoutAndRender(afContext, facetObj);
      facetBounds = dvt.AfComponentFactory.getBounds(facetObj);
    }
    if (minHeight && facetBounds.h < minHeight) {
      afContext.setAvailableHeight(minHeight);
      facetAvailBounds = {w: availWidth, h: minHeight};
      dvt.AfComponentFactory.layoutAndRender(afContext, facetObj);
      facetBounds = dvt.AfComponentFactory.getBounds(facetObj);
    }

  }
  return {facetObj: facetObj, facetAvailBounds: facetAvailBounds, facetBounds: facetBounds};
};

/**
 * store context information in a dvt.BaseComponentEvent event or dispatch event .
 * @param {Object} event
 */
dvt.AdfDiagramNode.prototype.eventContextCallback = function(event) {
  // store clientRowKey in this event
  if (event) {
    event[dvt.BaseComponentEvent.CLIENT_ROW_KEY] = this.getData().getId();
  }

  this.GetDiagram().HideNodeTransientToolbar();
};

dvt.AdfDiagramNode.prototype.getContextCallback = function() {
  return this.eventContextCallback;
};

dvt.AdfDiagramNode.prototype.getContextCallbackObj = function() {
  return this;
};

dvt.AdfDiagramNode.prototype.isDoubleClickable = function() {
  return this.getData().getHasAnchorListener();
};


/**
 *  Set Partially Expanded state
 *  @param {boolean} bPartialExpand  true if partially expanded
 */
dvt.AdfDiagramNode.prototype.setPartiallyExpanded = function(bPartialExpand) {
  //Store the partial expand state before changing it
  if (this._bPartialExpand != bPartialExpand)
    this.GetDiagram().setAutomationState(this.getData().getId(), 'partialExpand', this._bPartialExpand);

  this._bPartialExpand = bPartialExpand;

  if (this._partialExpandIndicator) {
    this._partialExpandIndicatorContainer.removeChild(this._partialExpandIndicator);
    this._partialExpandIndicator = null;
    this._partialExpandHandler = null;
  }
  if (bPartialExpand) {
    this.CreatePartialExpandIndicator();
  }
};


/**
 * Check if the node is partially expanded and has additional links
 * @return {boolean} true if the node is partially expanded
 */
dvt.AdfDiagramNode.prototype.isPartiallyExpanded = function() {
  return this._bPartialExpand;
};


/**
 * @protected
 */
dvt.AdfDiagramNode.prototype.CreatePartialExpandIndicator = function() {
  if (!this.getData().isShowNodeActions() || !this.getData().isShowIsolate()) {
    return;
  }
  var ww = 18;
  var hh = 18;
  var tx = 0;
  var ty = 0;
  this._partialExpandIndicator = new dvt.Image(this.getCtx(), this._diagram.getResourcesMap()['additionalLinksIndicatorEna'], 0, 0, ww, hh);

  var dims;
  var lastChild;
  if (this._stackUnderlay && (this._stackUnderlay instanceof dvt.Container) && (this._stackUnderlay.getNumChildren() > 0)) {
    lastChild = this._stackUnderlay.getChildAt(this._stackUnderlay.getNumChildren() - 1);
    if (lastChild.getWidth && lastChild.getHeight) {
      this._partialExpandIndicatorContainer = this._stackUnderlay;
      if (lastChild.getX && lastChild.getY) {
        tx = lastChild.getX();
        ty = lastChild.getY();
      }
      dims = new dvt.Rectangle(0, 0, lastChild.getWidth(), lastChild.getHeight());
    }
  }

  if (!dims) {
    this._partialExpandIndicatorContainer = this._getContentContainer();
    //use the saved node width and height, if possible, so that
    //overlays and fly outs don't affect dimensions
    if (this._nodeWidth && this._nodeHeight) {
      dims = new dvt.Rectangle(0, 0, this._nodeWidth, this._nodeHeight);
    }
    else {
      dims = this.getDimensionsWithStroke();
    }
  }

  tx += .5 * ((dims ? dims.w : 0) - ww);
  ty += (dims ? dims.h : 0) - .5 * hh;

  this._partialExpandIndicator.setTranslate(tx, ty);

  this._partialExpandHandler = new DvtAdfDiagramNodeExpandHandler(this.GetDiagram(), this.getData().getId());

  this.GetDiagram().getEventManager().associate(this._partialExpandIndicator, this._partialExpandHandler);

  this._partialExpandIndicatorContainer.addChild(this._partialExpandIndicator);

};


dvt.AdfDiagramNode.prototype._createCircle = function(cx, cy, radius, fillColor, strokeColor) {
  var circle = new dvt.Circle(this.getCtx(), cx, cy, radius);

  var fill = new dvt.SolidFill(fillColor, 1);
  circle.setFill(fill);

  var stroke = new dvt.SolidStroke(strokeColor);
  circle.setStroke(stroke);

  return circle;
};

/**
 * @override
 */
dvt.AdfDiagramNode.prototype.getLabelBounds = function() {
  //Bug 14186917: return saved label dimensions, because getting
  //them dynamically seems to return different sizes based on zoom
  var bounds = null;
  if (this._labelObj) {
    if (this._labelDims) {
      bounds = this._labelDims;
    }
    else {
      bounds = this._labelObj.getDimensions();
    }
  }
  return bounds;
};


/**
 * Set the label bounds in coordinates relative to this node.
 */
dvt.AdfDiagramNode.prototype.setLabelBounds = function(bounds) {
  if (this._labelObj && bounds)
  {
    //TO DO: handle wrapping, truncating, etc.
  }
};


/**
 * @override
 */
dvt.AdfDiagramNode.prototype.getLayoutBounds = function() {
  //Bug 13381683: save the outer bounds of the node and its overlays
  //in order to pass them to the layout engine
  if (this._layoutBounds) {
    return this._layoutBounds;
  }
  return this.getContentBounds();
};


/**
 * @override
 */
dvt.AdfDiagramNode.prototype.getContentBounds = function() {
  return new dvt.Rectangle(0, 0, this._nodeWidth, this._nodeHeight);
};


/**
 * @protected
 * Calc the layout bounds in coordinates relative to this node.
 */
dvt.AdfDiagramNode.prototype.CalcLayoutBounds = function() {
  var bounds = this.getContentBounds();

  if (this._containerBadge) {
    bounds = bounds.getUnion(this._containerBadge.getDimensions(this));
  }
  if (this._containerUnderlay) {
    bounds = bounds.getUnion(this._containerUnderlay.getDimensions(this));
  }
  if (this._stackBadge) {
    bounds = bounds.getUnion(this._stackBadge.getDimensions(this));
  }
  if (this._stackUnderlay) {
    bounds = bounds.getUnion(this._stackUnderlay.getDimensions(this));
  }
  if (this._partialExpandIndicator) {
    bounds = bounds.getUnion(this._partialExpandIndicator.getDimensions(this));
  }
  if (this._overlays) {
    for (var iOverlay = 0; iOverlay < this._overlays.length; iOverlay++) {
      var overlay = this._overlays[iOverlay]['overlay'];
      bounds = bounds.getUnion(overlay.getDimensions(this));
    }
  }
  return bounds;
};


/**
 * Returns an array of action buttons that reflects actions available for the node
 * @protected
 * @param {dvt.AdfDiagram} diagram A parent component for the node
 * @return {array} an array of action buttons
 */
dvt.AdfDiagramNode.prototype.GetFlyOutsArray = function(diagram) {
  var arButtons = [];
  var arFlags = [];

  //Add the buttons in the order you want them to appear
  //Bug 13745138: pay attention to showIsolate attribute
  //Bug 14851707: only show the isolate button if the isolate is valid,
  //meaning that the diagram will visibly change
  if (this.getData().isShowIsolate() && diagram.IsNodeIsolateValid(this.getData().getId())) {
    arFlags.push('isolate');
  }

  if (this.isContainer() && !this.isHiddenAncestor() && this.getData().isShowDrill()) {
    arFlags.push('drill');
  }

  if (this.isPartiallyExpanded() && this.getData().isShowIsolate()) {
    arFlags.push('partial_expand');
  }

  //don't include Show All button in node's toolbar because it's at
  //the top of the diagram
  /*if (this.getData().isShowIsolate()) {
    if ((diagram._isolatedNodes && diagram._isolatedNodes.length > 0)
   //don't need Show All button for drill, because we have breadcrumbs
   //     || diagram._drilledNodePath
       )
    {
      arFlags[SHOW_ALL] = true;
      numButtons++;
    }
  }*/

  if (this.getData().isShowStack()) {
    //TO DO: preview, explode, restore
    //preview, explode
    if (this.isStack()) {
      arFlags.push('stack_preview');
      arFlags.push('stack_explode');
    }
    //restack
    else {
      if (this.isRestackable()) {
        arFlags.push('stack_restack');
      }
    }
  }

  if (!diagram.isReadOnly() && this.isDeleteEnabled()) {
    arFlags.push('delete');
  }

  var cols = Math.min(arFlags.length, dvt.AdfDiagramNode._TRANSIENT_TOOLBAR_BUTTONS_PER_ROW);
  var rows = Math.ceil(arFlags.length / dvt.AdfDiagramNode._TRANSIENT_TOOLBAR_BUTTONS_PER_ROW);
  if (dvt.Agent.isRightToLeft(this.getCtx())) {
    arFlags.reverse();
  }
  for (var iFlag = 0; iFlag < arFlags.length; iFlag++) {
    var col = iFlag % dvt.AdfDiagramNode._TRANSIENT_TOOLBAR_BUTTONS_PER_ROW;
    var row = Math.floor(iFlag / dvt.AdfDiagramNode._TRANSIENT_TOOLBAR_BUTTONS_PER_ROW);
    //rounded top left corner on first button
    var bRoundTopLeft = (iFlag == 0);
    //rounded top right corner if button is in last column of top row,
    //or if last button in single row
    var bRoundTopRight =
        (iFlag == dvt.AdfDiagramNode._TRANSIENT_TOOLBAR_BUTTONS_PER_ROW - 1 ||
        (iFlag < dvt.AdfDiagramNode._TRANSIENT_TOOLBAR_BUTTONS_PER_ROW &&
        iFlag == arFlags.length - 1));
    //rounded bottom right corner if last button, or if button in last
    //column of next-to-last row with no button in last row directly
    //below it
    var bRoundBottomRight = (iFlag == arFlags.length - 1 ||
        (col == cols - 1 && row == rows - 2 && (arFlags.length % dvt.AdfDiagramNode._TRANSIENT_TOOLBAR_BUTTONS_PER_ROW) > 0));
    //rounded bottom left corner on button in first column of last row
    var bRoundBottomLeft = (col == 0 && row == rows - 1);

    switch (arFlags[iFlag]) {
      case 'isolate': //isolate
        var isolateButton = this.CreateIsolateFlyOut(bRoundTopLeft, bRoundTopRight, bRoundBottomLeft, bRoundBottomRight);
        arButtons.push(isolateButton);
        break;
      case 'container': //container
        var disclosureButton = null;
        if (this.isDisclosed()) {
          disclosureButton = this.CreateContainerCloseFlyOut(bRoundTopLeft, bRoundTopRight, bRoundBottomLeft, bRoundBottomRight);
        }
        else {
          disclosureButton = this.CreateContainerOpenFlyOut(bRoundTopLeft, bRoundTopRight, bRoundBottomLeft, bRoundBottomRight);
        }
        if (disclosureButton) {
          arButtons.push(disclosureButton);
        }
        break;
      case 'drill': //drill
        var drillButton = this.CreateDrillFlyOut(bRoundTopLeft, bRoundTopRight, bRoundBottomLeft, bRoundBottomRight);
        arButtons.push(drillButton);
        break;
      case 'partial_expand': //partial expand
        var partialExpandButton = this.CreatePartialExpandFlyOut(bRoundTopLeft, bRoundTopRight, bRoundBottomLeft, bRoundBottomRight);
        arButtons.push(partialExpandButton);
        break;
      case 'stack_preview': //stack preview
        var stackPreviewButton = this.CreateStackPreviewFlyOut(bRoundTopLeft, bRoundTopRight, bRoundBottomLeft, bRoundBottomRight);
        arButtons.push(stackPreviewButton);
        break;
      case 'stack_explode': //stack explode
        var stackExplodeButton = this.CreateStackExplodeFlyOut(bRoundTopLeft, bRoundTopRight, bRoundBottomLeft, bRoundBottomRight);
        arButtons.push(stackExplodeButton);
        break;
      case 'stack_restack': //stack restack
        var stackRestackButton = this.CreateStackRestackFlyOut(bRoundTopLeft, bRoundTopRight, bRoundBottomLeft, bRoundBottomRight);
        arButtons.push(stackRestackButton);
        break;
      case 'delete': //delete
        var deleteButton = this.CreateDeleteFlyOut(bRoundTopLeft, bRoundTopRight, bRoundBottomLeft, bRoundBottomRight);
        arButtons.push(deleteButton);
        break;
    }
  }

  return arButtons;
};

dvt.AdfDiagramNode.prototype.CreateFlyOuts = function(diagram) {
  var container = new dvt.Container(this.getCtx(), this.getData().getId() + '_flyOut');
  diagram.addChild(container);

  var arButtons = this.GetFlyOutsArray(diagram);
  for (var i = 0; i < arButtons.length; i++) {
    container.addChild(arButtons[i]);
  }

  var numButtons = arButtons.length;
  var rows = Math.ceil(numButtons / dvt.AdfDiagramNode._TRANSIENT_TOOLBAR_BUTTONS_PER_ROW);

  for (var i = 0; i < numButtons; i++) {
    var col = i % dvt.AdfDiagramNode._TRANSIENT_TOOLBAR_BUTTONS_PER_ROW;
    var row = Math.floor(i / dvt.AdfDiagramNode._TRANSIENT_TOOLBAR_BUTTONS_PER_ROW);
    var button = arButtons[i];
    //position buttons on negative Y axis so that flyouts appear
    //above mouse position, because container is placed with origin at
    //mouse position
    button.setTranslate(col * button.getWidth(), -rows * button.getHeight() + row * button.getHeight());
  }

  this._flyOuts = container;
};


/**
 * Used by automation APIs
 */
dvt.AdfDiagramNode.prototype.getFlyOutButtonById = function(id) {
  if (this._flyOuts) {
    var numChildren = this._flyOuts.getNumChildren();
    for (var i = 0; i < numChildren; i++) {
      var child = this._flyOuts.getChildAt(i);
      if (child.getId() == id)
        return child.getButton();
      if ((child.getId() == 'unstacking' || child.getId() == 'restacking')
           && id == 'stacking') {
        return child.getButton();
      }
    }
  }
  return null;
};


/**
 * Adds shadow effect to the transient toolbar
 * @protected
 */
dvt.AdfDiagramNode.prototype.ApplyTransientToolbarShadow = function() {
  /*
  // commented out shadow effect as it does not work consistently across different browsers
  var shadow = this.CreateTransientToolbarShadow();
  if (shadow && this._flyOuts) {
    this._flyOuts.addDrawEffect(shadow);
  }
  */
};


/**
 * Creates a shadow for the transient toolbar with the specified style
 * @return {dvt.Shadow} A shadow for the transient toolbar with the specified style
 * @protected
 */
dvt.AdfDiagramNode.prototype.CreateTransientToolbarShadow = function() {
  //shadow: 2 2 3 0 rgba(0, 0, 0, 0.5);
  var style = this._diagram.getDiagramButtonStyles();
  var shadowStyle = (style && style[dvt.PanZoomComponent.BOX_SHADOW]) ?
                    style[dvt.PanZoomComponent.BOX_SHADOW] : dvt.AdfDiagramNode._TOOLBAR_SHADOW_STYLE;
  var shadow = this._diagram.CreateDropShadowFilter(shadowStyle);
  return shadow;
};

dvt.AdfDiagramNode.prototype.RefreshTransientToolbar = function() {
  if (this.isFlyOutEnabled()) {
    //save the position of the flyouts because it will be cleared
    //when we hide them and we want to show them again at the same position
    var flyOutsPos = this._flyOutsPos;
    this.HideFlyOuts(true);
    this._flyOutsPos = flyOutsPos;
    this.ShowFlyOuts(true);
  }
};

dvt.AdfDiagramNode.prototype.ShowTransientToolbar = function(stagePos) {
  if (this.isFlyOutEnabled()) {
    this.CancelFlyInAnim();
    this._diagram.SetTransientToolbarDataId(this.getData().getId());
    this.ShowFlyOuts(false, stagePos);
  }
};

dvt.AdfDiagramNode.prototype.HideTransientToolbar = function(bImmediate) {
  this._diagram.SetTransientToolbarDataId(null);
  this.HideFlyOuts(bImmediate);
};


/**
 * Show  flyouts for the node
 * @param {boolean} immediate true to show flyout buttons immediately (no animation)
 * @param {dvt.Point} stagePos page coordinates relative to the stage
 * @protected
 */
dvt.AdfDiagramNode.prototype.ShowFlyOuts = function(immediate, stagePos) {
  if (this._flyOuts && stagePos) {
    this.PositionFlyOuts(stagePos);
  }

  if (this._flyOuts || this._flyOutAnim)
  {
    return;
  }

  var delay = .5;
  if (this.IsTransientToolbarOnClick()) {
    delay = 0;
  }

  if (this._flyInAnim)
  {
    if (this._flyInAnim.isRunning())
    {
      this._flyInAnim.stop(true);
    }
    this._flyInAnim = null;
    delay = 0;
  }

  var diagram = this.GetDiagram();
  this.CreateFlyOuts(diagram);
  this.PositionFlyOuts(stagePos);

  if (!immediate) {
    this._flyOutAnim = new dvt.AnimPopIn(this.getCtx(), this._flyOuts, true, .25, delay);

    dvt.Playable.appendOnEnd(this._flyOutAnim, this.OnFlyOutEnd, this);
    this._flyOutAnim.play();
  }
  else {
    //Bug 15936881: apply the shadow after animation ends so that the transient toolbar appears in FF17, which
    //means the shadow is no longer applied in CreateFlyOuts()
    this.ApplyTransientToolbarShadow();
  }
};


/**
 * Positions action toolbar
 * @protected
 * @param {dvt.Point} stagePos The coordinates relative to stage used for toolbar position
 */
dvt.AdfDiagramNode.prototype.PositionFlyOuts = function(stagePos) {
  var localPoint;
  if (stagePos) {
    var dims = this._flyOuts.getDimensions();
    var spacing = dvt.Agent.isTouchDevice() ? 17 : 7;
    var stagePoint = new dvt.Point(stagePos.x - .5 * dims.w, stagePos.y - spacing);

    //make sure the transient toolbar fits within the viewport
    dims.x = stagePoint.x;
    dims.y = stagePoint.y - dims.h;
    var edgePadding = 3;
    //if the toolbar fits vertically, just adjust it horizontally
    if (dims.y >= edgePadding) {
      //need to shift it right
      if (dims.x < edgePadding) {
        stagePoint.x = edgePadding;
      }
      //need to shift it left
      else if (dims.x + dims.w > this.GetDiagram().getWidth() - edgePadding) {
        stagePoint.x = this.GetDiagram().getWidth() - edgePadding - dims.w;
      }
    }
    //the toolbar needs to be shifted down
    else {
      stagePoint.y += edgePadding - dims.y;
      //shift horizontally to put it on the right of the cursor
      stagePoint.x = stagePos.x + spacing;
      //need to shift to put it on the left of the cursor
      if (stagePoint.x + dims.w > this.GetDiagram().getWidth() - edgePadding) {
        stagePoint.x = stagePos.x - spacing - dims.w;
      }
    }

    localPoint = this.GetDiagram().stageToLocal(stagePoint);
    this._flyOutsPos = localPoint;
  }
  else if (this._flyOutsPos) {
    localPoint = this._flyOutsPos;
  }
  else {
    localPoint = this.GetDiagram().stageToLocal(this.localToStage(new dvt.Point(0, 0)));
  }
  if (localPoint) {
    this._flyOuts.setTranslate(localPoint.x, localPoint.y);
  }
};

dvt.AdfDiagramNode.prototype.HideFlyOuts = function(immediate) {
  if (!this._flyOuts || this._flyInAnim)
  {
    return;
  }

  this._flyOutsPos = null;
  if (this._flyOutAnim)
  {
    if (this._flyOutAnim.isRunning())
    {
      //save and restore the current alpha after stopping
      //the animation so that flyouts don't appear immediately
      //when moving the mouse on and off the node quickly
      var currAlpha = this._flyOuts.getAlpha();
      this._flyOutAnim.stop(true);
      this._flyOuts.setAlpha(currAlpha);
    }
    this._flyOutAnim = null;
  }
  if (immediate) {
    this.OnFlyInEnd();
  }
  else {
    //Bug 16575172: remove the shadow before animating
    this._flyOuts.removeAllDrawEffects();
    //in Firefox, removing the shadow can leave rendering artifacts, so
    //poke the matrix in order to force a rerender to eliminate them
    if (this.getCtx().getDocumentUtils().fixGeckoContainerForFilter) {
      this.getCtx().getDocumentUtils().fixGeckoContainerForFilter(this._flyOuts.getParent());
    }
    //use a pop animation instead of alpha fade
    //this._flyInAnim = new dvt.Animator(this.getCtx(), .25);
    //this._flyInAnim.addProp(dvt.Animator.TYPE_NUMBER, this._flyOuts, this._flyOuts.getAlpha, this._flyOuts.setAlpha, 0);
    this._flyInAnim = new dvt.AnimPopOut(this.getCtx(), this._flyOuts, true, .25);

    this._flyInAnim.setOnEnd(this.OnFlyInEnd, this);
    this._flyInAnim.play();
  }
};

dvt.AdfDiagramNode.prototype.OnFlyOutEnd = function() {
  this._flyOutAnim = null;
  //Bug 15936881: apply the shadow after the animation ends so that the transient toolbar appears in FF17
  this.ApplyTransientToolbarShadow();
};

dvt.AdfDiagramNode.prototype.OnFlyInEnd = function() {
  if (this._bFlyInAnimCanceled) {
    return;
  }

  this._flyInAnim = null;

  if (this._flyOuts) {
    var parent = this._flyOuts.getParent();
    if (parent) {
      parent.removeChild(this._flyOuts);
    }
    if (this._flyOuts.destroy)
    {
      this._flyOuts.destroy();
    }
    this._flyOuts = null;
  }
  // Bug 14813675 - tooltips get stuck for isolate button and other buttons
  var tooltipManager = this.getCtx().getTooltipManager();
  if (tooltipManager) {
    tooltipManager.hideTooltip();
  }
};


/**
 * Creates an isolate button for the actions toolbar
 * @protected
 * @param {boolean} bRoundTopLeft True if top left corner should be rounded
 * @param {boolean} bRoundTopRight True if top right corner should be rounded
 * @param {boolean} bRoundBottomLeft True if bottom left corner should be rounded
 * @param {boolean} bRoundBottomRight True if bottom right corner should be rounded
 * @return {dvt.Button} an isolate button
 */
dvt.AdfDiagramNode.prototype.CreateIsolateFlyOut = function(bRoundTopLeft, bRoundTopRight, bRoundBottomLeft, bRoundBottomRight) {
  var isolateEna = this._diagram.getResourcesMap()['isolateEna'];
  var isolateOvr = this._diagram.getResourcesMap()['isolateOvr'];
  var isolateDwn = this._diagram.getResourcesMap()['isolateDwn'];
  var isolateTip = this._diagram.getResourcesMap()['isolateTip'];
  var button = this.CreateFlyOutButton(this.getCtx(), isolateEna, isolateOvr, isolateDwn, isolateTip,
                                       bRoundTopLeft, bRoundTopRight, bRoundBottomLeft, bRoundBottomRight,
                                       'isolate', this.HandleIsolate);
  return button;
};


/**
 * Creates a drill button for the actions toolbar
 * @protected
 * @param {boolean} bRoundTopLeft True if top left corner should be rounded
 * @param {boolean} bRoundTopRight True if top right corner should be rounded
 * @param {boolean} bRoundBottomLeft True if bottom left corner should be rounded
 * @param {boolean} bRoundBottomRight True if bottom right corner should be rounded
 * @return {dvt.Button} a drill button
 */
dvt.AdfDiagramNode.prototype.CreateDrillFlyOut = function(bRoundTopLeft, bRoundTopRight, bRoundBottomLeft, bRoundBottomRight) {
  var drillEna = this._diagram.getResourcesMap()['drillEna'];
  var drillOvr = this._diagram.getResourcesMap()['drillOvr'];
  var drillDwn = this._diagram.getResourcesMap()['drillDwn'];
  var drillTip = this._diagram.getResourcesMap()['drillTip'];
  var button = this.CreateFlyOutButton(this.getCtx(), drillEna, drillOvr, drillDwn, drillTip,
                                       bRoundTopLeft, bRoundTopRight, bRoundBottomLeft, bRoundBottomRight,
                                       'drill', this.HandleDrill);
  return button;
};


/**
 * Creates an additional links button for the actions toolbar
 * @protected
 * @param {boolean} bRoundTopLeft True if top left corner should be rounded
 * @param {boolean} bRoundTopRight True if top right corner should be rounded
 * @param {boolean} bRoundBottomLeft True if bottom left corner should be rounded
 * @param {boolean} bRoundBottomRight True if bottom right corner should be rounded
 * @return {dvt.Button} an additional links button
 */
dvt.AdfDiagramNode.prototype.CreatePartialExpandFlyOut = function(bRoundTopLeft, bRoundTopRight, bRoundBottomLeft, bRoundBottomRight) {
  var additionalLinksEna = this._diagram.getResourcesMap()['additionalLinksEna'];
  var additionalLinksOvr = this._diagram.getResourcesMap()['additionalLinksOvr'];
  var additionalLinksDwn = this._diagram.getResourcesMap()['additionalLinksDwn'];
  var additionalLinksTip = this._diagram.getResourcesMap()['additionalLinksTip'];
  var button = this.CreateFlyOutButton(this.getCtx(), additionalLinksEna, additionalLinksOvr, additionalLinksDwn, additionalLinksTip,
                                       bRoundTopLeft, bRoundTopRight, bRoundBottomLeft, bRoundBottomRight,
                                       'additionalLinks', this.HandlePartialExpand);
  return button;
};


/**
 * Creates a container open button for the actions toolbar
 * @protected
 * @param {boolean} bRoundTopLeft True if top left corner should be rounded
 * @param {boolean} bRoundTopRight True if top right corner should be rounded
 * @param {boolean} bRoundBottomLeft True if bottom left corner should be rounded
 * @param {boolean} bRoundBottomRight True if bottom right corner should be rounded
 * @return {dvt.Button} a container open button
 */
dvt.AdfDiagramNode.prototype.CreateContainerOpenFlyOut = function(bRoundTopLeft, bRoundTopRight, bRoundBottomLeft, bRoundBottomRight) {
  var collapsedEna = this._diagram.getResourcesMap()['discloseCollapsedEna'];
  var collapsedOvr = this._diagram.getResourcesMap()['discloseCollapsedOvr'];
  var collapsedDwn = this._diagram.getResourcesMap()['discloseCollapsedDwn'];
  var collapsedTip = this._diagram.getResourcesMap()['discloseCollapsedTip'];
  var button = this.CreateFlyOutButton(this.getCtx(), collapsedEna, collapsedOvr, collapsedDwn, collapsedTip,
                                       bRoundTopLeft, bRoundTopRight, bRoundBottomLeft, bRoundBottomRight,
                                       'disclose', this.HandleContainerOpen);
  return button;
};


/**
 * Creates a container close button for the actions toolbar
 * @protected
 * @param {boolean} bRoundTopLeft True if top left corner should be rounded
 * @param {boolean} bRoundTopRight True if top right corner should be rounded
 * @param {boolean} bRoundBottomLeft True if bottom left corner should be rounded
 * @param {boolean} bRoundBottomRight True if bottom right corner should be rounded
 * @return {dvt.Button} a container close button
 */
dvt.AdfDiagramNode.prototype.CreateContainerCloseFlyOut = function(bRoundTopLeft, bRoundTopRight, bRoundBottomLeft, bRoundBottomRight) {
  var expandedEna = this._diagram.getResourcesMap()['discloseExpandedEna'];
  var expandedOvr = this._diagram.getResourcesMap()['discloseExpandedOvr'];
  var expandedDwn = this._diagram.getResourcesMap()['discloseExpandedDwn'];
  var expandedTip = this._diagram.getResourcesMap()['discloseExpandedTip'];
  var button = this.CreateFlyOutButton(this.getCtx(), expandedEna, expandedOvr, expandedDwn, expandedTip,
                                       bRoundTopLeft, bRoundTopRight, bRoundBottomLeft, bRoundBottomRight,
                                       'disclose', this.HandleContainerClose);
  return button;
};


/**
 * Creates a stack preview button for the actions toolbar
 * @protected
 * @param {boolean} bRoundTopLeft True if top left corner should be rounded
 * @param {boolean} bRoundTopRight True if top right corner should be rounded
 * @param {boolean} bRoundBottomLeft True if bottom left corner should be rounded
 * @param {boolean} bRoundBottomRight True if bottom right corner should be rounded
 * @return {dvt.Button} a stack preview button
 */
dvt.AdfDiagramNode.prototype.CreateStackPreviewFlyOut = function(bRoundTopLeft, bRoundTopRight, bRoundBottomLeft, bRoundBottomRight) {
  var ena = this._diagram.getResourcesMap()['stackPreviewEna'];
  var ovr = this._diagram.getResourcesMap()['stackPreviewOvr'];
  var dwn = this._diagram.getResourcesMap()['stackPreviewDwn'];
  var tip = this._diagram.getResourcesMap()['stackPreviewTip'];
  var button = this.CreateFlyOutButton(this.getCtx(), ena, ovr, dwn, tip,
                                       bRoundTopLeft, bRoundTopRight, bRoundBottomLeft, bRoundBottomRight, 'stackPreview', this.HandleStackPreview);
  return button;
};


/**
 * Creates a stack explode button for the actions toolbar
 * @protected
 * @param {boolean} bRoundTopLeft True if top left corner should be rounded
 * @param {boolean} bRoundTopRight True if top right corner should be rounded
 * @param {boolean} bRoundBottomLeft True if bottom left corner should be rounded
 * @param {boolean} bRoundBottomRight True if bottom right corner should be rounded
 * @return {dvt.Button} a stack explode button
 */
dvt.AdfDiagramNode.prototype.CreateStackExplodeFlyOut = function(bRoundTopLeft, bRoundTopRight, bRoundBottomLeft, bRoundBottomRight) {
  var ena = this._diagram.getResourcesMap()['unstackEna'];
  var ovr = this._diagram.getResourcesMap()['unstackOvr'];
  var dwn = this._diagram.getResourcesMap()['unstackDwn'];
  var tip = this._diagram.getResourcesMap()['unstackTip'];
  var button = this.CreateFlyOutButton(this.getCtx(), ena, ovr, dwn, tip,
                                       bRoundTopLeft, bRoundTopRight, bRoundBottomLeft, bRoundBottomRight,
                                       'unstacking', this.HandleStackExplode);
  return button;
};


/**
 * Creates a stack restack button for the actions toolbar
 * @protected
 * @param {boolean} bRoundTopLeft True if top left corner should be rounded
 * @param {boolean} bRoundTopRight True if top right corner should be rounded
 * @param {boolean} bRoundBottomLeft True if bottom left corner should be rounded
 * @param {boolean} bRoundBottomRight True if bottom right corner should be rounded
 * @return {dvt.Button} a stack restack button
 */
dvt.AdfDiagramNode.prototype.CreateStackRestackFlyOut = function(bRoundTopLeft, bRoundTopRight, bRoundBottomLeft, bRoundBottomRight) {
  var ena = this._diagram.getResourcesMap()['restackEna'];
  var ovr = this._diagram.getResourcesMap()['restackOvr'];
  var dwn = this._diagram.getResourcesMap()['restackDwn'];
  var tip = this._diagram.getResourcesMap()['restackTip'];
  var button = this.CreateFlyOutButton(this.getCtx(), ena, ovr, dwn, tip,
                                       bRoundTopLeft, bRoundTopRight, bRoundBottomLeft, bRoundBottomRight,
                                       'restacking', this.HandleStackRestack);
  return button;
};


/**
 * Creates a delete button for the actions toolbar
 * @protected
 * @param {boolean} bRoundTopLeft True if top left corner should be rounded
 * @param {boolean} bRoundTopRight True if top right corner should be rounded
 * @param {boolean} bRoundBottomLeft True if bottom left corner should be rounded
 * @param {boolean} bRoundBottomRight True if bottom right corner should be rounded
 * @return {dvt.Button} a delete button
 */
dvt.AdfDiagramNode.prototype.CreateDeleteFlyOut = function(bRoundTopLeft, bRoundTopRight, bRoundBottomLeft, bRoundBottomRight) {
  var deleteEna = this._diagram.getResourcesMap()['deleteEna'];
  var deleteOvr = this._diagram.getResourcesMap()['deleteOvr'];
  var deleteDwn = this._diagram.getResourcesMap()['deleteDwn'];
  var deleteTip = this._diagram.getResourcesMap()['deleteTip'];
  var button = this.CreateFlyOutButton(this.getCtx(), deleteEna, deleteOvr, deleteDwn, deleteTip,
                                       bRoundTopLeft, bRoundTopRight, bRoundBottomLeft, bRoundBottomRight,
                                       'delete', this.HandleDelete);
  return button;
};


/**
 * Creates a flyout button for the actions toolbar
 * @protected
 * @param {dvt.Context} context Platform specific context object
 * @param {string} iconEna URL to the button image in enabled state
 * @param {string} iconOvr URL to the button image in hover state
 * @param {string} iconDwn URL to the button image in pressed down state
 * @param {string} tooltip Button tooltip
 * @param {boolean} bRoundTopLeft True if top left corner should be rounded
 * @param {boolean} bRoundTopRight True if top right corner should be rounded
 * @param {boolean} bRoundBottomLeft True if bottom left corner should be rounded
 * @param {boolean} bRoundBottomRight True if bottom right corner should be rounded
 * @param {function} callback The function that should be called on click event
 * @return {dvt.Button} a flyout button
 */
dvt.AdfDiagramNode.prototype.CreateFlyOutButton = function(context, iconEna, iconOvr, iconDwn, tooltip, 
    bRoundTopLeft, bRoundTopRight, bRoundBottomLeft, bRoundBottomRight, id, 
    callback) {
  var button = new DvtAdfDiagramFlyOutButton(context, iconEna, iconOvr, iconDwn, tooltip, this.GetDiagram().getEventManager(),
      bRoundTopLeft, bRoundTopRight, bRoundBottomLeft, bRoundBottomRight,
      this._diagram.getDiagramButtonStyles(), id, callback, this);
  if (!dvt.Agent.isTouchDevice()) {
    button.addEvtListener(dvt.MouseEvent.MOUSEOVER, this.HandleFlyOutMouseOver, false, this);
    button.addEvtListener(dvt.MouseEvent.MOUSEOUT, this.HandleFlyOutMouseOut, false, this);
  }
  return button;
};

dvt.AdfDiagramNode.prototype.HandleFlyOutMouseOver = function(evt) {
  if (!this.IsTransientToolbarOnClick()) {
    this.CancelFlyInAnim();
  }
};

dvt.AdfDiagramNode.prototype.HandleFlyOutMouseOut = function(evt) {
  if (!this.IsTransientToolbarOnClick()) {
    this.HideFlyOuts();
  }
};

dvt.AdfDiagramNode.prototype.CancelFlyInAnim = function(evt) {
  if (this._flyInAnim)
  {
    if (this._flyInAnim.isRunning())
    {
      this._bFlyInAnimCanceled = true;
      this._flyInAnim.stop();
      this._bFlyInAnimCanceled = false;
    }
    this._flyInAnim = null;
  }
  if (this._flyOuts) {
    if (!this._flyOutAnim || !this._flyOutAnim.isRunning()) {
      this._flyOuts.setAlpha(dvt.AdfDiagramNode._TRANSIENT_TOOLBAR_ALPHA);
    }
  }
};

/**
 * Handles an isolate event
 *
 * @param {dvt.BaseEvent} evt the event
 * @param {dvt.Button} source
 * @param {boolean} suppressAnimation whether animation should be suppressed or not
 * @param {number} degrees the number of degrees of isolation
 */
dvt.AdfDiagramNode.prototype.HandleIsolate = function(evt, source, suppressAnimation, degrees) {
  dvt.EventManager.consumeEvent(evt);
  degrees = degrees ? degrees : 0;
  if (this._bStack) {
    this.GetDiagram().isolateStack(this.getData().getId(), degrees, dvt.AdfDiagram.ISOLATE_TYPE_INTERSECT, suppressAnimation);
  }
  else {
    this.GetDiagram().isolateNode(this.getData().getId(), degrees, dvt.AdfDiagram.ISOLATE_TYPE_INTERSECT, suppressAnimation);
  }
  this.ClickedFlyOutButton();
};

/**
 * Handles a drillevent
 *
 * @param {dvt.BaseEvent} evt the event
 * @param {dvt.Button} source
 * @param {boolean} suppressAnimation whether animation should be suppressed or not
 */
dvt.AdfDiagramNode.prototype.HandleDrill = function(evt, source, suppressAnimation) {
  dvt.EventManager.consumeEvent(evt);
  this.GetDiagram().drillNode(this.getData().getId(), suppressAnimation);
  this.ClickedFlyOutButton();
};

dvt.AdfDiagramNode.prototype.HandlePartialExpand = function(evt) {
  dvt.EventManager.consumeEvent(evt);
  // We should treat this as a stack as long as we are currently stacking and this node's stackName isn't exploded
  // i.e. even stacks of size 1 should be treated as stacks
  var diagram = this.GetDiagram();
  var treatAsStack = false;
  var attributeGroups = diagram.getAttributeGroups();
  if (attributeGroups) {
    var stackName = diagram.CreateStackName(this.getData().getId(), attributeGroups);
    if (stackName) {
      var explodedStacks = diagram.getExplodedStacks();
      if (!explodedStacks || dvt.ArrayUtils.getIndex(explodedStacks, stackName) == -1) {
        treatAsStack = true;
      }
    }
  }
  if (treatAsStack) {
    diagram.isolateStack(this.getData().getId(), 1, dvt.AdfDiagram.ISOLATE_TYPE_UNION);
  }
  else {
    diagram.isolateNode(this.getData().getId(), 1, dvt.AdfDiagram.ISOLATE_TYPE_UNION);
  }
  this.ClickedFlyOutButton();
};

/**
 * Handles a restore event
 *
 * @param {dvt.BaseEvent} evt the event
 * @param {dvt.Button} source
 * @param {boolean} suppressAnimation whether animation should be suppressed or not
 */

dvt.AdfDiagramNode.prototype.HandleRestore = function(evt, source, suppressAnimation) {
  dvt.EventManager.consumeEvent(evt);
  this.GetDiagram().restore(suppressAnimation);
  this.ClickedFlyOutButton();
};

/**
 * Handles a container open event
 *
 * @param {dvt.BaseEvent} event the event
 * @param {dvt.Button} source
 * @param {boolean} suppressAnimation whether animation should be suppressed or not
 */
dvt.AdfDiagramNode.prototype.HandleContainerOpen = function(event, source, suppressAnimation)
{
  //Bug 13381888: consume the event
  dvt.EventManager.consumeEvent(event);
  var nodeId = this.getData().getId();
  this.GetDiagram().setNodeDisclosed(nodeId, true, suppressAnimation);
  this.ClickedFlyOutButton();
};

/**
 * Handles a container close event
 *
 * @param {dvt.BaseEvent} event the event
 * @param {dvt.Button} source
 * @param {boolean} suppressAnimation whether animation should be suppressed or not
 */
dvt.AdfDiagramNode.prototype.HandleContainerClose = function(event, source, suppressAnimation)
{
  //Bug 13381888: consume the event
  dvt.EventManager.consumeEvent(event);
  var nodeId = this.getData().getId();
  this.GetDiagram().setNodeDisclosed(nodeId, false, suppressAnimation);
  this.ClickedFlyOutButton();
};

dvt.AdfDiagramNode.prototype.HandleStackPreview = function(evt) {
  dvt.EventManager.consumeEvent(evt);
  this._stackBadgeHandler.ShowDialog(evt);
  this.ClickedFlyOutButton();
};

dvt.AdfDiagramNode.prototype.HandleDelete = function(evt) {
  dvt.EventManager.consumeEvent(evt);
  var nodes = [this.getData().getId()];
  var event = new dvt.AdfDiagramDeleteNodeEvent(nodes);
  this.GetDiagram().dispatchEvent(event);
};


//Bug 15838158: need to be able to hide and show stack preview when component is resized
dvt.AdfDiagramNode.prototype.ShowStackPreview = function(bImmediate) {
  this._stackBadgeHandler.ShowDialog(null, bImmediate);
};

/**
 * Hides stack preview dialog
 * @param {boolean} bImmediate true to skip animation
 */
dvt.AdfDiagramNode.prototype.HideStackPreview = function(bImmediate) {
  this._stackBadgeHandler.CloseDialog(false, bImmediate);
};

dvt.AdfDiagramNode.prototype.HandleStackExplode = function(evt) {
  dvt.EventManager.consumeEvent(evt);
  this._stackBadgeHandler.ExplodeStack();
  this.ClickedFlyOutButton();
};

dvt.AdfDiagramNode.prototype.HandleStackRestack = function(evt) {
  dvt.EventManager.consumeEvent(evt);
  var attrGrps = this._diagram.getAttributeGroups();
  var explodedStacks = this._diagram.getExplodedStacks();
  if (attrGrps && attrGrps.length > 0 && explodedStacks && explodedStacks.length > 0) {
    //can't use this.GetStackId() because this node is not actually
    //in a stack at the moment
    var stackId = this._diagram.CreateStackName(this.getData().getId(), this._diagram.getAttributeGroups());
    for (var iStack = 0; iStack < explodedStacks.length; iStack++) {
      if (stackId == explodedStacks[iStack]) {
        explodedStacks.splice(iStack, 1);
        this._diagram.setExplodedStacks(explodedStacks);
        break;
      }
    }
  }
  this.ClickedFlyOutButton();
};

dvt.AdfDiagramNode.prototype.ClickedFlyOutButton = function() {
  //  if (!this.IsTransientToolbarOnClick()) {
  //this.HideFlyOuts();
  this.HideTransientToolbar();
  //  }
  //  else {
  //    this.RefreshTransientToolbar();
  //  }
};

dvt.AdfDiagramNode.prototype.setFlyOutEnabled = function(bEnabled) {
  this._bFlyOutEnabled = bEnabled;
};

dvt.AdfDiagramNode.prototype.isFlyOutEnabled = function() {
  return this._bFlyOutEnabled && this.getData().isShowNodeActions();
};

dvt.AdfDiagramNode.prototype.SetContainerBadgeVisible = function(bVisible) {
  if (this.isContainer() && this._containerBadge) {
    this._containerBadge.setVisible(bVisible);
    this._containerBadge.setMouseEnabled(bVisible);
  }
};

dvt.AdfDiagramNode.prototype.RefreshContainerBadgeVisibility = function() {
  if (this.isContainer() && this._containerBadge) {
    //hide the container badge when the view is zoomed out
    this.SetContainerBadgeVisible(this._diagram.getPanZoomCanvas().getZoom() >= dvt.AdfDiagramNode._CONTAINER_BADGE_MIN_ZOOM);
  }
};


/**
 * Rerenders the expand/collapse button
 * @protected
 */
dvt.AdfDiagramNode.prototype.RefreshContainerBadge = function() {
  if (this._containerBadge)
  {
    this._getContentContainer().removeChild(this._containerBadge);
    this._containerBadge = null;
    this._containerBadgeHandler = null;
  }
  if (this.isContainer())
  {
    if (this.getData().isShowExpandChildren()) {
      this.CreateContainerBadge();
      //hide the container badge when the view is zoomed out
      this.RefreshContainerBadgeVisibility();
    }
  }
};


/**
 * @protected
 * Helper method that creates a container for open/close button depending on the state of the node
 */
dvt.AdfDiagramNode.prototype.CreateContainerBadge = function() {
  var iconX = 0;
  var iconY = 0;
  var iconWidth = 20;
  var iconHeight = 20;
  var context = this.getCtx();

  var iconEna = null;
  var iconOvr = null;
  var iconDwn = null;

  var id;
  if (!this.isDisclosed()) {
    iconEna = this._diagram.getResourcesMap()['discloseCollapsedEna'];
    iconOvr = this._diagram.getResourcesMap()['discloseCollapsedOvr'];
    iconDwn = this._diagram.getResourcesMap()['discloseCollapsedDwn'];
    id = 'disclosure';
  }
  else {
    iconEna = this._diagram.getResourcesMap()['discloseExpandedEna'];
    iconOvr = this._diagram.getResourcesMap()['discloseExpandedOvr'];
    iconDwn = this._diagram.getResourcesMap()['discloseExpandedDwn'];
    id = 'disclosure';
  }

  var upState = new dvt.Image(context, iconEna, iconX, iconY, iconWidth, iconHeight);
  var overState = new dvt.Image(context, iconOvr, iconX, iconY, iconWidth, iconHeight);
  var downState = new dvt.Image(context, iconDwn, iconX, iconY, iconWidth, iconHeight);

  var button = new dvt.Button(context, upState, overState, downState, null, id);


  this._getContentContainer().addChild(button);

  this._containerBadge = button;
  this.PositionContainerBadge();

  this._containerBadgeHandler = new DvtAdfDiagramContainerBadgeHandler(this.GetDiagram(), this, button);
  this.GetDiagram().getEventManager().associate(this._containerBadge, this._containerBadgeHandler);
};


/**
 * @protected
 * Helper method that positions container badge
 * @param {dvt.Animator} animator The animator into which the transitions should be rendered (optional)
 */
dvt.AdfDiagramNode.prototype.PositionContainerBadge = function(animator) {
  var badge = this.getContainerBadge();
  if (badge) {
    if (!dvt.Agent.isRightToLeft(this.getCtx()))
      badge.setTranslateX(0);
    else {
      var nodeBounds = this.getContentBounds();
      var width = badge.getDimensions() ? badge.getDimensions().w : 0;
      var posX = nodeBounds.w - width;
      if (animator)
        animator.addProp(dvt.Animator.TYPE_NUMBER, badge, badge.getTranslateX, badge.setTranslateX, posX);
      else
        badge.setTranslateX(posX);
    }
    badge.setTranslateY(0);
  }
};


/**
 * Used by automation APIs
 */
dvt.AdfDiagramNode.prototype.getContainerBadge = function(animator) {
  return this._containerBadge;
};


/**
 * Gets dimensions of the child node pane for the disclosed container
 * @param {dvt.Animator} animator (optional) the animator into which this node should be rendered
 * @protected
 */
dvt.AdfDiagramNode.prototype.CalcChildNodePaneBounds = function(animator) {
  var bounds;
  if (this._childNodePane) {

    var diagram = this.GetDiagram();
    var i;
    var maxX;
    var maxY;
    var minX;
    var minY;
    var currMinX;
    var currMinY;
    var currMaxX;
    var currMaxY;
    var childId;
    var child;
    var labelBounds;
    var labelPos;
    var labelRotAngle;
    var labelRotPoint;
    for (i = 0; i < this._childNodeIds.length; i++) {
      childId = this._childNodeIds[i];
      child = diagram.getNodeById(childId);
      if (!child.getVisible()) {
        continue;
      }

      var childBounds = child.getLayoutBounds().clone();
      var childX = animator ? animator.getDestVal(child, child.getTranslateX, true) : child.getTranslateX();
      var childY = animator ? animator.getDestVal(child, child.getTranslateY, true) : child.getTranslateY();
      childBounds.x += childX;
      childBounds.y += childY;

      currMaxX = childBounds.x + childBounds.w;
      currMinX = childBounds.x;
      currMaxY = childBounds.y + childBounds.h;
      currMinY = childBounds.y;

      //Add stroke width when node is disclosed and has a clipPath
      if (child.isDisclosed() && child._containerChrome) {
        var halfStrokeWidth = child._containerChrome.getStroke().getWidth() / 2;
        currMinX -= halfStrokeWidth;
        currMinY -= halfStrokeWidth;
        currMaxX += halfStrokeWidth;
        currMaxY += halfStrokeWidth;
      }

      labelBounds = animator ? animator.getDestVal(child, child.getLabelBounds, true) : child.getLabelBounds();
      labelPos = animator ? animator.getDestVal(child, child.getLabelPosition, true) : child.getLabelPosition();
      if (labelBounds && labelPos) {
        labelRotAngle = animator ? animator.getDestVal(child, child.getLabelRotationAngle, true) : child.getLabelRotationAngle();
        labelRotPoint = animator ? animator.getDestVal(child, child.getLabelRotationPoint, true) : child.getLabelRotationPoint();
        //Bug 16370352: take label rotation into account when sizing child node pane
        var relLabelBounds = child.GetRelativeLabelBounds(labelPos, labelBounds, labelRotAngle, labelRotPoint);
        //label coords are relative to containing node, so always
        //need to add childX,Y to labelPos.x,y
        currMaxX = relLabelBounds.x + childX + relLabelBounds.w;
        currMaxY = relLabelBounds.y + childY + relLabelBounds.h;
        currMinX = relLabelBounds.x + childX;
        currMinY = relLabelBounds.y + childY;

      }
      if (maxX) {
        minX = Math.min(minX, currMinX);
        maxX = Math.max(maxX, currMaxX);
        minY = Math.min(minY, currMinY);
        maxY = Math.max(maxY, currMaxY);
      }
      else {
        minX = currMinX;
        maxX = currMaxX;
        minY = currMinY;
        maxY = currMaxY;
      }

    }
    bounds = new dvt.Rectangle(minX, minY, maxX - minX, maxY - minY);
    if (this._childLinkIds) {
      this.CalcLinkBounds(this._childLinkIds, bounds, animator);
    }
    if (this._crossChildLinkIds) {
      this.CalcLinkBounds(this._crossChildLinkIds, bounds, animator);
    }
  }
  if (!bounds) {
    bounds = new dvt.Rectangle(0, 0, 0, 0);
  }
  return bounds;
};


/**
 * Calculate link bounds
 * @param {array} arLinkIds array of link ids
 * @param {dvt.Rectangle} bounds link bounds
 * @param {dvt.Animator} animator (optional) the animator into which this node should be rendered
 * @protected
 */
dvt.AdfDiagramNode.prototype.CalcLinkBounds = function(arLinkIds, bounds, animator) {
  var maxX;
  var maxY;
  var minX;
  var minY;
  var labelBounds;
  var labelPos;
  var labelRotAngle;
  var labelRotPoint;
  var diagram = this.GetDiagram();
  for (var i = 0; i < arLinkIds.length; i++) {
    var childId = arLinkIds[i];
    var child = diagram.getLinkById(childId);
    if (!child.getVisible()) {
      continue;
    }

    var dims = child.getDimensionsWithStroke(animator);
    if (dims) {
      maxX = Math.max(bounds.x + bounds.w, dims.x + dims.w);
      maxY = Math.max(bounds.y + bounds.h, dims.y + dims.h);
      minX = Math.min(bounds.x, dims.x);
      minY = Math.min(bounds.y, dims.y);

      bounds.x = minX;
      bounds.y = minY;
      bounds.w = maxX - minX;
      bounds.h = maxY - minY;
    }

    labelBounds = animator ? animator.getDestVal(child, child.getLabelBounds, true) : child.getLabelBounds();
    labelPos = animator ? animator.getDestVal(child, child.getLabelPosition, true) : child.getLabelPosition();
    if (labelBounds && labelPos) {
      labelRotAngle = animator ? animator.getDestVal(child, child.getLabelRotationAngle, true) : child.getLabelRotationAngle();
      labelRotPoint = animator ? animator.getDestVal(child, child.getLabelRotationPoint, true) : child.getLabelRotationPoint();
      //Bug 16370352: take label rotation into account when sizing child node pane
      var relLabelBounds = child.GetRelativeLabelBounds(labelPos, labelBounds, labelRotAngle, labelRotPoint);
      maxX = Math.max(relLabelBounds.x + relLabelBounds.w, bounds.x + bounds.w);
      maxY = Math.max(relLabelBounds.y + relLabelBounds.h, bounds.y + bounds.h);
      minX = Math.min(bounds.x, relLabelBounds.x);
      minY = Math.min(bounds.y, relLabelBounds.y);

      bounds.x = minX;
      bounds.y = minY;
      bounds.w = maxX - minX;
      bounds.h = maxY - minY;
    }
  }
};


/**
 * @override
 */
dvt.AdfDiagramNode.prototype.isDragAvailable = function(clientIds) {
  if (this.isDraggable())
    return this.GetDiagram().__isDragAvailable(clientIds);
  else
    return null;
};


/**
 * @override
 */
dvt.AdfDiagramNode.prototype.getDragTransferable = function(mouseX, mouseY) {
  return this.GetDiagram().__getDragTransferable(this);
};


/**
 * @override
 */
dvt.AdfDiagramNode.prototype.getDragFeedback = function(mouseX, mouseY) {
  return this.GetDiagram().__getDragFeedback();
};


/**
 * Returns a displayable used for drop site feedback.
 * @return {dvt.Displayable}
 */
dvt.AdfDiagramNode.prototype.getDropSiteFeedback = function() {
  var layoutBounds = this.getLayoutBounds();
  var topLeft = new dvt.Point(layoutBounds.x, layoutBounds.y);
  var bottomRight = new dvt.Point(layoutBounds.x + layoutBounds.w, layoutBounds.y + layoutBounds.h);
  topLeft = this.localToStage(topLeft);
  bottomRight = this.localToStage(bottomRight);
  return new dvt.Rect(this.getCtx(), topLeft.x, topLeft.y, bottomRight.x - topLeft.x, bottomRight.y - topLeft.y);
};


/**
 * Returns the displayable for this node.
 */
dvt.AdfDiagramNode.prototype.__getDisplayable = function() {
  //this.HideFlyOuts(true);
  this.HideTransientToolbar(true);
  var editor = this.GetDiagram().getEditor();
  if (editor)
    editor.processDragEnd(null, { x: 0, y: 0});
  return this;
};


/**
 * @protected
 */
dvt.AdfDiagramNode.prototype.CreateLabel = function(afContext) {
  var label = null;
  var labelTemplate = this._nodeDef.getLabelTemplate();
  if (labelTemplate)
  {
    label = dvt.AfComponentFactory.parseAndStamp(afContext, labelTemplate, this);
  }
  else if (this._label)
  {
    var afLabel = new dvt.AfOutputText();
    afLabel.setValue(this._label);
    if (this._nodeLabelStyles)
      afLabel.setInlineStyle(this._nodeLabelStyles.toString());
    label = dvt.AfComponentFactory.parseAndStamp(afContext, afLabel, this);
  }
  return label;
};

dvt.AdfDiagramNode.prototype.HasLabel = function() {
  return (this._label || this._nodeDef.getLabelTemplate());
};

/**
 * @protected
 * Get label bounds relative to this node, taking rotation and position into account.
 */
dvt.AdfDiagramNode.prototype.GetRelativeLabelBounds = function(pos, bounds, rotAngle, rotPoint) {
  var mat = dvt.BaseDiagramNode.CalcLabelMatrix(pos, bounds, rotAngle, rotPoint);
  var topLeft = mat.transformPoint(new dvt.Point(bounds.x, bounds.y));
  var topRight = mat.transformPoint(new dvt.Point(bounds.x + bounds.w, bounds.y));
  var bottomLeft = mat.transformPoint(new dvt.Point(bounds.x, bounds.y + bounds.h));
  var bottomRight = mat.transformPoint(new dvt.Point(bounds.x + bounds.w, bounds.y + bounds.h));
  var minX = Math.min(Math.min(topLeft.x, topRight.x), Math.min(bottomLeft.x, bottomRight.x));
  var maxX = Math.max(Math.max(topLeft.x, topRight.x), Math.max(bottomLeft.x, bottomRight.x));
  var minY = Math.min(Math.min(topLeft.y, topRight.y), Math.min(bottomLeft.y, bottomRight.y));
  var maxY = Math.max(Math.max(topLeft.y, topRight.y), Math.max(bottomLeft.y, bottomRight.y));
  return new dvt.Rectangle(minX, minY, maxX - minX, maxY - minY);
};


dvt.AdfDiagramNode.prototype.getContainerTopHeight = function() {
  if (this._containerTopBounds) {
    return this._containerTopBounds.h;
  }
  return 0;
};

dvt.AdfDiagramNode.prototype.getContainerLeftWidth = function() {
  if (this._containerLeftBounds) {
    return this._containerLeftBounds.w;
  }
  return 0;
};


/**
 * Sets padding for a container node
 * Default Values will be used when values are missing.
 * @param {object} containerPadding object containing container padding values
 * @param {dvt.Animator} animator The animator into which the transitions should be rendered (optional)
 */
dvt.AdfDiagramNode.prototype.setContainerPadding = function(containerPadding, animator) {
  //console.log(this.getShortDesc() + " " + this.getData().getId() + ": " + top + ", " + right + ", " + bottom + ", " + left);
  if (this._containerPadding &&
      containerPadding &&
      this._containerPadding.top == containerPadding.top &&
      this._containerPadding.right == containerPadding.right &&
      this._containerPadding.bottom == containerPadding.bottom &&
      this._containerPadding.left == containerPadding.left) {
    //nothing changed, so return
    return;
  }
  var oldContainerPadding = this._containerPadding;
  this._containerPadding = {};
  this._containerPadding.top = containerPadding && containerPadding.top != null ? containerPadding.top : dvt.AdfDiagramNode._CONTAINER_PADDING;
  this._containerPadding.right = containerPadding && containerPadding.right != null ? containerPadding.right : dvt.AdfDiagramNode._CONTAINER_PADDING;
  this._containerPadding.bottom = containerPadding && containerPadding.bottom != null ? containerPadding.bottom : dvt.AdfDiagramNode._CONTAINER_PADDING;
  this._containerPadding.left = containerPadding && containerPadding.left != null ? containerPadding.left : dvt.AdfDiagramNode._CONTAINER_PADDING;

  //Bug 14823204: if we don't have child node pane dims, then the node hasn't been rendered yet,
  //so just return
  if (!this._childNodePaneDims) {
    return;
  }
  this.TransitionContainerPadding(oldContainerPadding, this._containerPadding, animator);

  //update container badge Position
  this.PositionContainerBadge(animator);

  //update overview node for container padding
  this.GetDiagram().UpdateOverviewNode(this, animator);
};

dvt.AdfDiagramNode.prototype.getContainerPadding = function() {
  return this._containerPadding;
};


dvt.AdfDiagramNode.prototype.SetMaxAlpha = function(maxAlpha) {
  this._maxAlpha = maxAlpha;
};

dvt.AdfDiagramNode.prototype.GetMaxAlpha = function() {
  return this._maxAlpha;
};

dvt.AdfDiagramNode.prototype._getContentContainer = function() {
  return this._contentPane ? this._contentPane : this;
};

// Implementation of DvtKeyboardNavigable interface


/**
 * @override
 */
dvt.AdfDiagramNode.prototype.getNextNavigable = function(event) 
{
  var nextObject = null;

  //Bug 15838130: don't change focus if this is a drill ancestor
  if (this.isHiddenAncestor()) {
    return this.GetDiagram().getEventManager().getFocus();
  }

  if (event.type == dvt.MouseEvent.CLICK)
  {
    return this;
  }
  else if (event.keyCode == dvt.KeyboardEvent.SPACE && event.ctrlKey)
  {
    // multi-select node with current focus; so we navigate to ourself and then let the selection handler take
    // care of the selection
    return this;
  }
  else if (event.keyCode == dvt.KeyboardEvent.OPEN_BRACKET) { //next node down in container hierarchy
    if (this.isContainer()) {
      var childNodeIds = this.getChildNodeIds();
      if (childNodeIds && childNodeIds.length > 0) {
        for (var childIndex = 0; childIndex < childNodeIds.length; childIndex++) {
          var childNode = this.GetDiagram().getNodeById(childNodeIds[childIndex]);
          if (childNode.getVisible())
            return childNode;
        }
      }
    }
    return this;
  }
  else if (event.keyCode == dvt.KeyboardEvent.CLOSE_BRACKET) {//next node up in container hierarchy
    var groupId = this.getData().getGroupId();
    if (groupId) {
      var parent = this.GetDiagram().getNodeById(groupId);
      if (parent.getVisible() && !parent.isHiddenAncestor())
        return parent;
    }
    return this;
  }
  else if ((event.keyCode == dvt.KeyboardEvent.OPEN_ANGLED_BRACKET || dvt.KeyboardEvent.CLOSE_ANGLED_BRACKET) &&
      event.altKey) { //select link if exists
    var adjLinks = this.GetDiagram().getNavigableLinksForNodeId(this.getData().getId());
    var keyboardHandler = this._diagram.getEventManager().getKeyboardHandler();
    if (keyboardHandler && keyboardHandler.getFirstNavigableLink)
      nextObject = keyboardHandler.getFirstNavigableLink(this, event, adjLinks);
    if (nextObject)
      nextObject.setKeyboardFocusNode(this);
    else
      nextObject = this;
    return nextObject;
  }

  var arNodes = [];
  var groupId = this.getData().getGroupId();
  if (groupId) {
    var parent = this.GetDiagram().getNodeById(groupId);
    var arChildIds = parent.getChildNodeIds();
    for (var i = 0; i < arChildIds.length; i++) {
      var childNode = this.GetDiagram().getNodeById(arChildIds[i]);
      if (!childNode.getVisible())
        continue;
      arNodes.push(childNode);
    }
  }
  else {
    var nodesByGroupDepth = this._diagram.getSortedVisibleNodesByGroupDepth();
    arNodes = nodesByGroupDepth[0];
  }

  nextObject = dvt.KeyboardHandler.getNextAdjacentNavigable(this, event, arNodes);
  if (nextObject) {
    var editor = this.GetDiagram().getEditor();
    if (editor && editor.getLinkCreateWithKeyboard() && this._diagram.isLinkToolEnabled()) {
      editor.processLinkCreateWithKeyboard(nextObject);
    }
  }
  return nextObject;
};


/**
 * @protected
 */
dvt.AdfDiagramNode.FindGroupDepth = function(node, nodesByGroupDepth) {
  for (var i = 0; i < nodesByGroupDepth.length; i++) {
    var nodesAtDepth = nodesByGroupDepth[i];
    for (var j = 0; j < nodesAtDepth.length; j++) {
      if (node == nodesAtDepth[j]) {
        return i;
      }
    }
  }
  return -1;
};

/**
 * @override
 * @param {boolean=} bFocusEffectOnly optional parameter if true, only visual focus effect shown, all other component state unchanged
 *                                     param needed to show focus effect when tabbing to a node
 */
dvt.AdfDiagramNode.prototype.showKeyboardFocusEffect = function(bFocusEffectOnly) 
{
  if (!bFocusEffectOnly) {
    this.GetDiagram().HideNodeTransientToolbar(true); // hide any existing toolbar before transferring focus
  }
  this.ShowKeyboardFocusEffect(bFocusEffectOnly);
  if (!bFocusEffectOnly) {
    this._bFocusEffect = true;
    this.setFocused(true);
    this._curTabStopIdx = 0;
  }
};


/**
 * Show keyboard focus effects
 * @protected
 * @param {boolean=} bFocusEffectOnly optional parameter if true, only visual focus effect shown, all other component state unchanged
 *                                     param needed to show focus effect when tabbing to a node
 */
dvt.AdfDiagramNode.prototype.ShowKeyboardFocusEffect = function(bFocusEffectOnly)  {
  this.showHoverEffect();
  if (!this.IsTransientToolbarOnClick() && !bFocusEffectOnly) {
    if (this.isFlyOutEnabled())
    {
      this.CancelFlyInAnim();
      this.ShowFlyOuts();
    }
  }
};


/**
 * @override
 * @param {boolean=} bFocusEffectOnly optional parameter if true, only visual focus effect removed, all other component state unchanged
 *                                     param needed to hide focus effect when tabbing to a node
 */
dvt.AdfDiagramNode.prototype.hideKeyboardFocusEffect = function(bFocusEffectOnly) 
{
  var oldFocusEffect = this._bFocusEffect;
  this._bFocusEffect = false;
  this.HideKeyboardFocusEffect(bFocusEffectOnly);
  if (bFocusEffectOnly) {
    this._bFocusEffect = oldFocusEffect;
  }
  else {
    this.resetTabStops();
    this.setFocused(false);
  }
};


/**
 * Hide keyboard focus effects
 * @protected
 * @param {boolean=} bFocusEffectOnly optional parameter if true, only visual focus effect removed, all other component state unchanged
 *                                     param needed to hide focus effect when tabbing to a node
 */
dvt.AdfDiagramNode.prototype.HideKeyboardFocusEffect = function(bFocusEffectOnly) 
{
  if (!this._hover) {
    this.hideHoverEffect();
  }
  //don't hide the flyouts if it requires a click
  if (!this.IsTransientToolbarOnClick() && !bFocusEffectOnly) {
    this.HideFlyOuts(true);
  }
};


/**
 * @override
 */
dvt.AdfDiagramNode.prototype.isShowingKeyboardFocusEffect = function() 
{
  return this._bFocusEffect;
};

/**
 * Get the root of Af Component
 * @return {DvtAfObject}  Af component root
 */
dvt.AdfDiagramNode.prototype.GetAfComponentRoot = function() 
{
  return dvt.AfComponentFactory.getCompTree(this._afRoot);
};

/**
 * Get AF component from the Diagram node for the passed component id
 * @param {String} componentId  component id
 * @return {dvt.AfComponent} Af Component
 */
dvt.AdfDiagramNode.prototype.getAfComponent = function(componentId) {
  var rootComp = this.GetAfComponentRoot();
  var afComp = dvt.AfComponentFactory.findAfComponent(rootComp, componentId);
  if (afComp)
    return afComp;
  // check overlays
  if (this._overlays) {
    for (var i = 0; i < this._overlays.length; i++) {
      var overlay = this._overlays[i]['overlay'];
      rootComp = dvt.AfComponentFactory.getAfComponent(overlay);
      afComp = dvt.AfComponentFactory.findAfComponent(rootComp, componentId);
      if (afComp)
        return afComp;
    }
  }
  if (this.isDisclosed()) {
    if (this._containerTopObj) {
      rootComp = dvt.AfComponentFactory.getAfComponent(this._containerTopObj);
      afComp = dvt.AfComponentFactory.findAfComponent(rootComp, componentId);
      if (afComp)
        return afComp;
    }
    if (this._containerBottomObj) {
      rootComp = dvt.AfComponentFactory.getAfComponent(this._containerBottomObj);
      afComp = dvt.AfComponentFactory.findAfComponent(rootComp, componentId);
      if (afComp)
        return afComp;
    }
    if (this._containerRightObj) {
      rootComp = dvt.AfComponentFactory.getAfComponent(this._containerRightObj);
      afComp = dvt.AfComponentFactory.findAfComponent(rootComp, componentId);
      if (afComp)
        return afComp;
    }
    if (this._containerLeftObj) {
      rootComp = dvt.AfComponentFactory.getAfComponent(this._containerLeftObj);
      afComp = dvt.AfComponentFactory.findAfComponent(rootComp, componentId);
      if (afComp)
        return afComp;
    }
  }
  return null;
};

/**
 * Get show popup behaviors
 * @return {array} array of ShowPopupBehavior
 */
dvt.AdfDiagramNode.prototype.getShowPopupBehaviors = function() {
  if (!this._showPopupBehaviors && this._nodeDef.getShowPopupBehaviors()) {
    var afShowPopupBehaviors = this._nodeDef.getShowPopupBehaviors();
    var afContext = this.GetDiagram().createAfContext(this.getData().getId());
    this._showPopupBehaviors = [];
    for (var i = 0; i < afShowPopupBehaviors.length; i++) {
      this._showPopupBehaviors[i] = dvt.AfComponentFactory.parseAndStamp(afContext, afShowPopupBehaviors[i], null);
    }
  }
  return this._showPopupBehaviors;
};


/**
 * Get Client Behaviors
 * @return {array} Array of dvt.AfClientBehavior
 */
dvt.AdfDiagramNode.prototype.getClientBehaviors = function() {
  if (this.isDisclosed()) {
    // Don't associate behaviors with container whitespace
    return null;
  }
  if (!this._clientBehaviors && this._nodeDef.getClientBehaviors()) {
    var afClientBehaviors = this._nodeDef.getClientBehaviors();
    var afContext = this.GetDiagram().createAfContext(this.getData().getId());
    this._clientBehaviors = [];
    for (var i = 0; i < afClientBehaviors.length; i++) {
      this._clientBehaviors[i] = dvt.AfComponentFactory.parseAndStamp(afContext, afClientBehaviors[i], null);
    }
  }
  return this._clientBehaviors;
};

//Bug 13971862: return the bounds of the object to use for aligning popups
dvt.AdfDiagramNode.prototype.getPopupBounds = function(behavior) {
  if (behavior) {
    var alignId = behavior.getAlignId();
    if (alignId) {
      //if the alignId is the node itself, use the content bounds
      var templateId = this.getNodeDef().getId();
      if (templateId == alignId) {
        return this.GetNodePopupBounds();
      }
    }
  }
  return null;
};

//Bug 13971862: return the bounds of the object to use for aligning popups
dvt.AdfDiagramNode.prototype.GetNodePopupBounds = function() {
  var contentBounds = this.getContentBounds();
  var stageP1 = this.localToStage(new dvt.Point(contentBounds.x, contentBounds.y));
  var stageP2 = this.localToStage(new dvt.Point(contentBounds.x + contentBounds.w, contentBounds.y + contentBounds.h));

  return new dvt.Rectangle(stageP1.x, stageP1.y, stageP2.x - stageP1.x, stageP2.y - stageP1.y);
};

//Bug 13971862: find the bounds to use for aligning a popup from a component in the node
dvt.AdfDiagramNode.prototype.findComponentPopupBounds = function(alignId) {
  //if the alignId is the node itself, use the content bounds
  var templateId = this.getNodeDef().getId();
  if (templateId == alignId) {
    return this.GetNodePopupBounds();
  }

  var compTemplate = null;
  //if the node is a disclosed container, search through the container facets
  if (this.isDisclosed()) {
    if (this._containerTopObj) {
      var topTemplate = this._containerTopObj._compTree;
      if (topTemplate) {
        compTemplate = dvt.AdfDiagramNode.FindAfComponent(alignId, topTemplate);
      }
    }
    if (!compTemplate && this._containerBottomObj) {
      var bottomTemplate = this._containerBottomObj._compTree;
      if (bottomTemplate) {
        compTemplate = dvt.AdfDiagramNode.FindAfComponent(alignId, bottomTemplate);
      }
    }
    if (!compTemplate && this._containerLeftObj) {
      var leftTemplate = this._containerLeftObj._compTree;
      if (leftTemplate) {
        compTemplate = dvt.AdfDiagramNode.FindAfComponent(alignId, leftTemplate);
      }
    }
    if (!compTemplate && this._containerRightObj) {
      var rightTemplate = this._containerRightObj._compTree;
      if (rightTemplate) {
        compTemplate = dvt.AdfDiagramNode.FindAfComponent(alignId, rightTemplate);
      }
    }

  }
  //search through the components in the node
  else {
    if (this._afRoot) {
      var template = this._afRoot._compTree;
      if (template) {
        compTemplate = dvt.AdfDiagramNode.FindAfComponent(alignId, template);
      }
    }
  }
  if (compTemplate) {
    var dispObj = compTemplate.getDisplayObj();
    if (dispObj) {
      return dispObj.getDimensions(dispObj.getCtx().getStage());
    }
  }

  return null;
};

dvt.AdfDiagramNode.FindAfComponent = function(id, template) {
  if (id == template.getId()) {
    return template;
  }

  var numChildren = template.getNumChildren();
  for (var i = 0; i < numChildren; i++) {
    var childTemplate = dvt.AdfDiagramNode.FindAfComponent(id, template.getChildAt(i));
    if (childTemplate) {
      return childTemplate;
    }
  }

  return null;
};


dvt.AdfDiagramNode.prototype.setStateProp = function(key, val) {
  this._stateProps[key] = val;
};

dvt.AdfDiagramNode.prototype.getStateProp = function(key) {
  return this._stateProps[key];
};

dvt.AdfDiagramNode.prototype._setContainerFacetScale = function(animator, obj, sx, sy) {
  if (animator) {
    animator.addProp(dvt.Animator.TYPE_NUMBER, obj, obj.getScaleX, obj.setScaleX, sx);
    animator.addProp(dvt.Animator.TYPE_NUMBER, obj, obj.getScaleY, obj.setScaleY, sy);
  }
  else {
    obj.setScale(sx, sy);
  }
};

dvt.AdfDiagramNode.prototype._setContainerFacetTranslate = function(animator, obj, tx, ty) {
  if (animator) {
    animator.addProp(dvt.Animator.TYPE_NUMBER, obj, obj.getTranslateX, obj.setTranslateX, tx);
    animator.addProp(dvt.Animator.TYPE_NUMBER, obj, obj.getTranslateY, obj.setTranslateY, ty);
  }
  else {
    obj.setTranslate(tx, ty);
  }
};

dvt.AdfDiagramNode.prototype.getContainerStyle = function() {
  return this._containerStyle;
};

dvt.AdfDiagramNode.prototype.setContainerStyle = function(containerStyle) {
  this._containerStyle = containerStyle;
};
/**
 * @return {dvt.CSSStyle} style of node of overview window
 */
dvt.AdfDiagramNode.prototype.getOverviewStyle = function() {
  return this._overviewStyle;
};
/**
 * @param {dvt.CSSStyle} overviewStyle style of node of overview window
 */
dvt.AdfDiagramNode.prototype.setOverviewStyle = function(overviewStyle) {
  this._overviewStyle = overviewStyle;
};
/**
 * @return {dvt.CSSStyle} style of disclosed node of overview window
 */
dvt.AdfDiagramNode.prototype.getOverviewContainerStyle = function() {
  return this._overviewContainerStyle;
};
/**
 * @param {dvt.CSSStyle} overviewContainerStyle style of disclosed node of overview window
 */
dvt.AdfDiagramNode.prototype.setOverviewContainerStyle = function(overviewContainerStyle) {
  this._overviewContainerStyle = overviewContainerStyle;
};
/**
 * @private
 * @return {string} returns the container background color
 */
dvt.AdfDiagramNode.prototype._getContainerBgColor = function() {
  if (this._containerStyle) {
    var color = this._containerStyle.getStyle(dvt.CSSStyle.BACKGROUND_COLOR);
    if (color)
      return color;
  }
  return this._defaultContainerBgColor;
};
/**
 * @private
 * @return {string} returns the container border color
 */
dvt.AdfDiagramNode.prototype._getContainerBorderColor = function() {
  if (this._containerStyle) {
    var color = this._containerStyle.getStyle(dvt.CSSStyle.BORDER_COLOR);
    if (color)
      return color;
  }
  return this._defaultContainerBorderColor;
};
/**
 * @private
 * @return {string} returns the container border radius
 */
dvt.AdfDiagramNode.prototype._getContainerBorderRx = function() {
  if (this._containerStyle) {
    var rx = this._containerStyle.getStyle(dvt.CSSStyle.BORDER_RADIUS);
    if (rx) {
      return dvt.CSSStyle.toNumber(rx);
    }
  }
  return this._defaultContainerBorderRx;
};
/**
 * @private
 * @return {string} returns the container border width
 */
dvt.AdfDiagramNode.prototype._getContainerBorderWidth = function() {
  if (this._containerStyle) {
    var width = this._containerStyle.getStyle(dvt.CSSStyle.BORDER_WIDTH);
    if (width) {
      return dvt.CSSStyle.toNumber(width);
    }
  }
  return null;
};

dvt.AdfDiagramNode.prototype.applyBadgeTextStyles = function(text) {
  var cssStyle = new dvt.CSSStyle();
  cssStyle.setStyle(dvt.CSSStyle.FONT_FAMILY, dvt.AfStyleUtils.getDefaultFontFamily());

  var skinStyles = this._diagram.getStackBadgeStyles();
  if (skinStyles)
    cssStyle.merge(skinStyles);
  text.setCSSStyle(cssStyle);
};


/**
 * Helper methods that applies styles on stack container badge
 * @param {dvt.Rect} rect Rectangle for the stack container badge
 */
dvt.AdfDiagramNode.prototype.applyBadgeStyles = function(rect) {

  var bgColor, borderColor, rx;

  var skinStyles = this._diagram.getStackBadgeStyles();
  if (skinStyles) {
    bgColor = skinStyles.getStyle(dvt.CSSStyle.BACKGROUND_COLOR);
    borderColor = skinStyles.getStyle(dvt.CSSStyle.BORDER_COLOR);
    rx = skinStyles.getStyle(dvt.CSSStyle.BORDER_RADIUS);
    if (rx)
      rx = dvt.CSSStyle.toNumber(rx);
  }

  var fill = new dvt.SolidFill(bgColor);
  rect.setFill(fill);

  if (borderColor) {
    var stroke = this._getUnderlayStroke(borderColor);
    rect.setStroke(stroke);
  }
  if (rx) {
    rect.setRx(rx);
    rect.setRy(rx);
  }
  rect.setPixelHinting(true);
};

/**
 * Get ancestors of current node
 * @return {array}  array of ancestor nodes
 */
dvt.AdfDiagramNode.prototype.getAncestors = function() {
  var ancestors = [];
  var groupId = this.getData().getGroupId();
  if (groupId) {
    var parent = this.GetDiagram().getNodeById(groupId);
    ancestors = parent.getAncestors();
    ancestors.splice(0, 0, groupId); // insert groupId as the first element of the ancestor array
  }
  return ancestors;
};


/**
 * Gets states for the node that are going to be used for WAI-ARIA label
 * @return {array} array of states for the node
 */
dvt.AdfDiagramNode.prototype.getAccessibilityStates = function() {
  var states = [];
  if (this.isSelectable()) {
    states.push(dvt.Bundle.getTranslatedString(dvt.Bundle.UTIL_PREFIX, this.isSelected() ? 'STATE_SELECTED' : 'STATE_UNSELECTED'));
  }

  if (this.isContainer()) {
    states.push(this.isDisclosed() ? dvt.Bundle.getTranslatedString(dvt.Bundle.UTIL_PREFIX, 'STATE_EXPANDED') :
        dvt.Bundle.getTranslatedString(dvt.Bundle.UTIL_PREFIX, 'STATE_COLLAPSED'));
  }

  if (this.isStack()) {
    states.push(dvt.Bundle.getTranslatedString(dvt.Bundle.ADF_DIAGRAM_PREFIX, 'STATE_STACKED'));
    states.push(dvt.Bundle.getTranslatedString(dvt.Bundle.UTIL_PREFIX, 'COLON_SEP_LIST', [dvt.Bundle.getTranslatedString(dvt.Bundle.ADF_DIAGRAM_PREFIX, 'SIZE'), this.getStackCount()]));
  }

  if (this.isPartiallyExpanded() && this.getData().isShowIsolate()) {
    states.push(dvt.Bundle.getTranslatedString(dvt.Bundle.ADF_DIAGRAM_PREFIX, 'STATE_HAS_ADDITIONAL_LINKS'));
  }

  return states;
};


/**
 * @override
 */
dvt.AdfDiagramNode.prototype.getAriaLabel = function() {
  return dvt.Displayable.generateAriaLabel(this.getShortDesc(), this.getAccessibilityStates());
};


/**
 * Updates WAI-ARIA attributes for the node
 * @protected
 */
dvt.AdfDiagramNode.prototype.UpdateAccessibilityAttributes = function() {
  if (!dvt.Agent.deferAriaCreation()) {
    var desc = this.getAriaLabel();
    if (desc) {
      this.setAriaProperty('label', desc);
    }
  }
};


/**
 * Adds WAI-ARIA attributes to the node
 */
dvt.AdfDiagramNode.prototype.addAccessibilityAttributes = function() {
  // WAI-ARIA
  this.setAriaRole('group');
  if (!dvt.Agent.deferAriaCreation()) {
    var desc = this.getAriaLabel();
    if (desc) {
      this.setAriaProperty('label', desc);
    }
  }
};


/**
 * Keyboard event handler
 * @param {dvt.KeyboardEvent} event keyboard event
 * @protected
 */
dvt.AdfDiagramNode.prototype.HandleKeyboardEvent = function(event) {
  var keyCode = event.keyCode;

  if (keyCode == dvt.KeyboardEvent.ENTER) {
    //finish link creation if it was started
    var editor = this.GetDiagram().getEditor();
    if (editor && editor.getLinkCreateWithKeyboard() && this._diagram.isLinkToolEnabled()) {
      var editorEvents = new Array();
      editor.processLinkCreateEnd(this, null, editorEvents);
      for (var i = 0; i < editorEvents.length; i++) {
        this.GetDiagram().dispatchEvent(editorEvents[i]);
      }
    }
    else if (event.altKey) { //preview stack
      if (this.getData().isShowStack() && this.isStack())
        this.HandleStackPreview(null);
    }
    else if (event.shiftKey) { //isolate
      if (this.getData().isShowIsolate() && this.GetDiagram().IsNodeIsolateValid(this.getData().getId()))
        this.HandleIsolate(null);
      else if (this.getData().isShowIsolate())
        this.HandleRestore(null);
    }
    else { //drill
      if (this.isContainer() && !this.isHiddenAncestor() && this.getData().isShowDrill())
        this.HandleDrill(null);
      else {
        //if drill is not available, show popup if it is defined on the node
        var popupBehaviors = this.getShowPopupBehaviors();
        if (popupBehaviors) {
          var eventManager = this.GetDiagram().getEventManager();
          var point = this.localToStage(new dvt.Point(0, 0));
          var mouseEvent = dvt.DomEventFactory.generateMouseEventFromKeyboardEvent(event, this.getCtx(), dvt.MouseEvent.CLICK,
              this.getCtx().getStage(),
              point.x, point.y);
          eventManager.getPopupHandler().processPopupHelper(this,
              popupBehaviors,
              [dvt.ShowPopupBehavior.TRIGGER_TYPE_ACTION, dvt.ShowPopupBehavior.TRIGGER_TYPE_CLICK],
              new dvt.Point(mouseEvent.pageX, mouseEvent.pageY),
              mouseEvent);
        }
      }
    }
  }
  else if (keyCode == dvt.KeyboardEvent.SINGLE_QUOTE) { //show additional links
    if (this.isPartiallyExpanded() && this.getData().isShowIsolate())
      this.HandlePartialExpand(null);
  }
  else if (keyCode == dvt.KeyboardEvent.SPACE && event.ctrlKey && event.shiftKey) { //open/close node
    if (this.isContainer()) {
      var nodeId = this.getData().getId();
      this._diagram.setNodeDisclosed(nodeId, !this.isDisclosed());
    }
  }
  else if (dvt.KeyboardEvent.isSemicolon(event) && event.shiftKey) { //unstack
    if (this.getData().isShowStack() && this.isStack()) {
      if (this._stackBadgeHandler && this._stackBadgeHandler.isDialogVisible())
        this._stackBadgeHandler.CloseDialog(true);
      else
        this.HandleStackExplode(null);
    }
  }
  else if (dvt.KeyboardEvent.isSemicolon(event)) { //restack
    if (this.isRestackable())
      this.HandleStackRestack(null);
  }
  else if (keyCode == dvt.KeyboardEvent.ESCAPE) { //close stack preview dialog
    if (this.getData().isShowStack() && this.isStack() &&
        this._stackBadgeHandler && this._stackBadgeHandler.isDialogVisible())
      this._stackBadgeHandler.CloseDialog();
  }
};

/**
 * @protected
 * Copy the state to this diagram node from the passed node
 * @param {dvt.AdfDiagramNode} fromNode  Diagram node to copy from
 */
dvt.AdfDiagramNode.prototype.CopyState = function(fromNode) {
  this._nodeWidth = fromNode._nodeWidth;
  this._nodeHeight = fromNode._nodeHeight;
  this._labelDims = fromNode._labelDims;

  this._hover = fromNode._hover;

  this._bStack = fromNode._bStack;
  this._bDisclosed = fromNode._bDisclosed;
  this._bPartialExpand = fromNode._bPartialExpand;
  this._nodeWidth100 = fromNode._nodeWidth100;
  this._nodeHeight100 = fromNode._nodeHeight100;
  this._bHiddenAncestor = fromNode._bHiddenAncestor;
  this._bVisible = fromNode._bVisible;

  //don't save selected state because we need to reapply selection through
  //the selectionHandler in dvt.AdfDiagramTransitionUtils._animateTransitionDataHandler,
  //and that won't work if the node reports that it's already selected
  //this._selected = fromNode._selected;
  //don't save the hover/focused state - it will be reapplied using focus object stored by event manager
  //this._bHoverEffect = fromNode._bHoverEffect;
  //this._bFocused = fromNode._bFocused;
  //this._bFocusEffect = fromNode._bFocusEffect;

  this._stateProps = fromNode._stateProps;
};

dvt.AdfDiagramNode.prototype.destroy = function() {
  this.DestroyDisclosedContent(this);
  this.DestroyUndisclosedContent(this);

  dvt.AdfDiagramNode.superclass.destroy.call(this);
};

dvt.AdfDiagramNode.prototype.CreateCustomAnimPlayable = function() {
  return new dvt.CustomAnimation(this.getCtx(), null, this.GetDiagram().getAnimationDuration());
};


/**
 * Shows animated transformation of the node from old state to new
 * @param {DvtAdfDiagramDataAnimationHandler} dataAnimHandler Animation handler
 * @param {dvt.AdfDiagramNode} oldNode Old node
 */
dvt.AdfDiagramNode.prototype.AnimateUpdate = function(dataAnimHandler, oldNode) {
  var playable = null;

  //if visibility is changing, treat the update like an insert or delete,
  //and don't have to consider any other changes to the node
  var oldVisible = oldNode.getVisible();
  var newVisible = this.getVisible();
  if (oldVisible != newVisible) {
    if (!playable) {
      playable = this.CreateCustomAnimPlayable();
    }
    //don't process the descendants separately because the visibility
    //change will handle them
    dataAnimHandler.SkipDescendants(this.getData().getId(), this.GetDiagram());
    if (oldVisible) {
      //treat like a delete
      var nodeParent = this.GetParentContainer();
      oldNode.InsertNodeIntoParent(nodeParent);
      playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, oldNode, oldNode.getAlpha, oldNode.setAlpha, 0);
      dvt.Playable.appendOnEnd(playable, function() {
        oldNode.getParent().removeChild(oldNode);
        oldNode.destroy();
      });
      //moving into a stack
      if (this._dataAnimStackPos) {
        playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, oldNode, oldNode.getTranslateX, oldNode.setTranslateX, this._dataAnimStackPos.x);
        playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, oldNode, oldNode.getTranslateY, oldNode.setTranslateY, this._dataAnimStackPos.y);
        this._dataAnimStackPos = null;
      }
      dataAnimHandler.add(playable, DvtAdfDiagramDataAnimationHandler.PHASE_DELETE);
    }
    else if (newVisible) {
      //treat like an insert
      this.setAlpha(0);
      playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, this, this.getAlpha, this.setAlpha, this.GetMaxAlpha());
      //moving out of a stack
      if (this._dataAnimStackPos) {
        var newTx = this.getTranslateX();
        var newTy = this.getTranslateY();
        this.setTranslate(this._dataAnimStackPos.x, this._dataAnimStackPos.y);
        playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, this, this.getTranslateX, this.setTranslateX, newTx);
        playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, this, this.getTranslateY, this.setTranslateY, newTy);
        this._dataAnimStackPos = null;
      }
      dataAnimHandler.add(playable, DvtAdfDiagramDataAnimationHandler.PHASE_INSERT);
    }

    //the node is either appearing or disappearing, so no need to
    //worry about other changes
    //playable = null;
    return;
  }

  //need to handle translate before disclosure, because disclosure
  //transition will return afterwards
  var oldTx = oldNode.getTranslateX();
  var oldTy = oldNode.getTranslateY();
  var newTx = this.getTranslateX();
  var newTy = this.getTranslateY();
  if (oldTx != newTx) {
    if (!playable) {
      playable = this.CreateCustomAnimPlayable();
    }
    this.setTranslateX(oldTx);
    playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, this, this.getTranslateX, this.setTranslateX, newTx);
  }
  if (oldTy != newTy) {
    if (!playable) {
      playable = this.CreateCustomAnimPlayable();
    }
    this.setTranslateY(oldTy);
    playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, this, this.getTranslateY, this.setTranslateY, newTy);
  }

  //handle label before disclosure, because disclosure transition will
  //return afterwards
  if (oldNode._labelObj && this._labelObj) {
    var oldMat = oldNode._labelObj.getMatrix();
    var newMat = this._labelObj.getMatrix();
    if (!oldMat.equals(newMat)) {
      if (!playable) {
        playable = this.CreateCustomAnimPlayable();
      }
      this._labelObj.setMatrix(oldMat);
      playable.getAnimator().addProp(dvt.Animator.TYPE_MATRIX, this._labelObj, this._labelObj.getMatrix, this._labelObj.setMatrix, newMat);
    }
  }

  //if disclosure changed, return immediately afterwards without
  //processing any more diffs
  var oldDisclosed = oldNode.isDisclosed();
  var newDisclosed = this.isDisclosed();
  var bDisclosureChanged = (oldDisclosed != newDisclosed);
  if (bDisclosureChanged) {
    if (!playable) {
      playable = this.CreateCustomAnimPlayable();
    }
    //don't process the descendants separately because the disclosure
    //will handle them
    dataAnimHandler.SkipDescendants(this.getData().getId(), this.GetDiagram());
    this.AnimateUpdateDisclosure(dataAnimHandler, oldNode, playable.getAnimator(), newTx, newTy);
    dataAnimHandler.add(playable, DvtAdfDiagramDataAnimationHandler.PHASE_UPDATE);
    //if disclosure changed, don't need to process any more diffs
    return;
  }

  if (oldNode.isHiddenAncestor() != this.isHiddenAncestor()) {
    var hiddenAncestorPlayable = this.CreateCustomAnimPlayable();
    this.AnimateUpdateHiddenAncestor(dataAnimHandler, hiddenAncestorPlayable.getAnimator());
    dataAnimHandler.add(hiddenAncestorPlayable, this.isHiddenAncestor() ? DvtAdfDiagramDataAnimationHandler.PHASE_DELETE : DvtAdfDiagramDataAnimationHandler.PHASE_INSERT);
  }

  if (this._childNodePane && oldNode._childNodePane) {
    var oldTx = oldNode._childNodePane.getTranslateX();
    var oldTy = oldNode._childNodePane.getTranslateY();
    var newTx = this._childNodePane.getTranslateX();
    var newTy = this._childNodePane.getTranslateY();
    if (oldTx != newTx) {
      if (!playable) {
        playable = this.CreateCustomAnimPlayable();
      }
      this._childNodePane.setTranslateX(oldTx);
      playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, this._childNodePane, this._childNodePane.getTranslateX, this._childNodePane.setTranslateX, newTx);
    }
    if (oldTy != newTy) {
      if (!playable) {
        playable = this.CreateCustomAnimPlayable();
      }
      this._childNodePane.setTranslateY(oldTy);
      playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, this._childNodePane, this._childNodePane.getTranslateY, this._childNodePane.setTranslateY, newTy);
    }
  }

  if (this._containerChrome) {
    var newNodeWidth = this._nodeWidth;
    var newNodeHeight = this._nodeHeight;
    var newClipPathDef = this._getContainerChromeClipPathDef();
    if (newNodeWidth != oldNode._nodeWidth) {
      if (!playable) {
        playable = this.CreateCustomAnimPlayable();
      }
      this._containerChrome.setWidth(oldNode._nodeWidth);
      playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, this._containerChrome, this._containerChrome.getWidth, this._containerChrome.setWidth, newNodeWidth);
    }
    if (newNodeHeight != oldNode._nodeHeight) {
      if (!playable) {
        playable = this.CreateCustomAnimPlayable();
      }
      this._containerChrome.setHeight(oldNode._nodeHeight);
      playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, this._containerChrome, this._containerChrome.getHeight, this._containerChrome.setHeight, newNodeHeight);
    }
    if ((newNodeWidth != oldNode._nodeWidth) || (newNodeHeight != oldNode._nodeHeight)) {
      if (!playable) {
        playable = this.CreateCustomAnimPlayable();
      }
      // update clippath
      this._setContainerChromeClipPath(oldNode._getContainerChromeClipPathDef());
      playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER_ARRAY, this, this._getContainerChromeClipPathDef, this._setContainerChromeClipPath, newClipPathDef);
    }
  }

  if (this._containerLeftObj) {
    if (!playable) {
      playable = this.CreateCustomAnimPlayable();
    }
    var sy = oldNode._nodeHeight / this._nodeHeight;
    this._setContainerFacetScale(null, this._containerLeftObj, 1, sy);
    this._setContainerFacetScale(playable.getAnimator(), this._containerLeftObj, 1, 1);
  }

  if (this._containerRightObj) {
    if (!playable) {
      playable = this.CreateCustomAnimPlayable();
    }
    var sy = oldNode._nodeHeight / this._nodeHeight;
    this._setContainerFacetScale(null, this._containerRightObj, 1, sy);
    this._setContainerFacetScale(playable.getAnimator(), this._containerRightObj, 1, 1);
  }

  if (this._containerTopObj) {
    if (!playable) {
      playable = this.CreateCustomAnimPlayable();
    }
    var sx = oldNode._nodeWidth / this._nodeWidth;
    this._setContainerFacetScale(null, this._containerTopObj, sx, 1);
    this._setContainerFacetScale(playable.getAnimator(), this._containerTopObj, 1, 1);
  }

  if (this._containerBottomObj) {
    if (!playable) {
      playable = this.CreateCustomAnimPlayable();
    }
    var sx = oldNode._nodeWidth / this._nodeWidth;
    this._setContainerFacetScale(null, this._containerBottomObj, sx, 1);
    this._setContainerFacetScale(playable.getAnimator(), this._containerBottomObj, 1, 1);
  }

  if (this._containerBadge && oldNode._containerBadge) {
    var containerBadgeX = this._containerBadge.getTranslateX();
    var containerBadgeY = this._containerBadge.getTranslateY();
    var oldBadgeX = oldNode._containerBadge.getTranslateX();
    var oldBadgeY = oldNode._containerBadge.getTranslateY();
    if (containerBadgeX != oldBadgeX) {
      if (!playable) {
        playable = this.CreateCustomAnimPlayable();
      }
      this._containerBadge.setTranslateX(oldBadgeX);
      playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, this._containerBadge, this._containerBadge.getTranslateX, this._containerBadge.setTranslateX, containerBadgeX);
    }
    if (containerBadgeY != oldBadgeY) {
      if (!playable) {
        playable = this.CreateCustomAnimPlayable();
      }
      this._containerBadge.setTranslateY(oldBadgeY);
      playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, this._containerBadge, this._containerBadge.getTranslateY, this._containerBadge.setTranslateY, containerBadgeY);
    }
  }

  if (this._partialExpandIndicator && oldNode._partialExpandIndicator) {
    var partialExpandIndicatorX = this._partialExpandIndicator.getTranslateX();
    var partialExpandIndicatorY = this._partialExpandIndicator.getTranslateY();
    var oldIndicatorX = oldNode._partialExpandIndicator.getTranslateX();
    var oldIndicatorY = oldNode._partialExpandIndicator.getTranslateY();
    if (partialExpandIndicatorX != oldIndicatorX) {
      if (!playable) {
        playable = this.CreateCustomAnimPlayable();
      }
      this._partialExpandIndicator.setTranslateX(oldIndicatorX);
      playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, this._partialExpandIndicator, this._partialExpandIndicator.getTranslateX, this._partialExpandIndicator.setTranslateX, partialExpandIndicatorX);
    }
    if (partialExpandIndicatorY != oldIndicatorY) {
      if (!playable) {
        playable = this.CreateCustomAnimPlayable();
      }
      this._partialExpandIndicator.setTranslateY(oldIndicatorY);
      playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, this._partialExpandIndicator, this._partialExpandIndicator.getTranslateY, this._partialExpandIndicator.setTranslateY, partialExpandIndicatorY);
    }
  }

  if (this._focusUnderlay && oldNode._focusUnderlay && this._focusUnderlay instanceof dvt.Rect) {
    var focusUnderlayX = this._focusUnderlay.getX();
    var focusUnderlayY = this._focusUnderlay.getY();
    var focusUnderlayWidth = this._focusUnderlay.getWidth();
    var focusUnderlayHeight = this._focusUnderlay.getHeight();
    var oldFocusUnderlayX = oldNode._focusUnderlay.getX();
    var oldFocusUnderlayY = oldNode._focusUnderlay.getY();
    var oldFocusUnderlayWidth = oldNode._focusUnderlay.getWidth();
    var oldFocusUnderlayHeight = oldNode._focusUnderlay.getHeight();
    if (focusUnderlayX != oldFocusUnderlayX) {
      if (!playable) {
        playable = this.CreateCustomAnimPlayable();
      }
      this._focusUnderlay.setX(oldFocusUnderlayX);
      playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, this._focusUnderlay, this._focusUnderlay.getX, this._focusUnderlay.setX, focusUnderlayX);
    }
    if (focusUnderlayY != oldFocusUnderlayY) {
      if (!playable) {
        playable = this.CreateCustomAnimPlayable();
      }
      this._focusUnderlay.setY(oldFocusUnderlayY);
      playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, this._focusUnderlay, this._focusUnderlay.getY, this._focusUnderlay.setY, focusUnderlayY);
    }
    if (focusUnderlayWidth != oldFocusUnderlayWidth) {
      if (!playable) {
        playable = this.CreateCustomAnimPlayable();
      }
      this._focusUnderlay.setWidth(oldFocusUnderlayWidth);
      playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, this._focusUnderlay, this._focusUnderlay.getWidth, this._focusUnderlay.setWidth, focusUnderlayWidth);
    }
    if (focusUnderlayHeight != oldFocusUnderlayHeight) {
      if (!playable) {
        playable = this.CreateCustomAnimPlayable();
      }
      this._focusUnderlay.setHeight(oldFocusUnderlayHeight);
      playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, this._focusUnderlay, this._focusUnderlay.getHeight, this._focusUnderlay.setHeight, focusUnderlayHeight);
    }
  }

  //Bug 15839721: update the hover/selection underlay
  if (this._nodeUnderlay && oldNode._nodeUnderlay) {
    var nodeUnderlayX = this._nodeUnderlay.getX();
    var nodeUnderlayY = this._nodeUnderlay.getY();
    var nodeUnderlayWidth = this._nodeUnderlay.getWidth();
    var nodeUnderlayHeight = this._nodeUnderlay.getHeight();
    var oldNodeUnderlayX = oldNode._nodeUnderlay.getX();
    var oldNodeUnderlayY = oldNode._nodeUnderlay.getY();
    var oldNodeUnderlayWidth = oldNode._nodeUnderlay.getWidth();
    var oldNodeUnderlayHeight = oldNode._nodeUnderlay.getHeight();
    if (nodeUnderlayX != oldNodeUnderlayX) {
      if (!playable) {
        playable = this.CreateCustomAnimPlayable();
      }
      this._nodeUnderlay.setX(oldNodeUnderlayX);
      playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, this._nodeUnderlay, this._nodeUnderlay.getX, this._nodeUnderlay.setX, nodeUnderlayX);
    }
    if (nodeUnderlayY != oldNodeUnderlayY) {
      if (!playable) {
        playable = this.CreateCustomAnimPlayable();
      }
      this._nodeUnderlay.setY(oldNodeUnderlayY);
      playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, this._nodeUnderlay, this._nodeUnderlay.getY, this._nodeUnderlay.setY, nodeUnderlayY);
    }
    if (nodeUnderlayWidth != oldNodeUnderlayWidth) {
      if (!playable) {
        playable = this.CreateCustomAnimPlayable();
      }
      this._nodeUnderlay.setWidth(oldNodeUnderlayWidth);
      playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, this._nodeUnderlay, this._nodeUnderlay.getWidth, this._nodeUnderlay.setWidth, nodeUnderlayWidth);
    }
    if (nodeUnderlayHeight != oldNodeUnderlayHeight) {
      if (!playable) {
        playable = this.CreateCustomAnimPlayable();
      }
      this._nodeUnderlay.setHeight(oldNodeUnderlayHeight);
      playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, this._nodeUnderlay, this._nodeUnderlay.getHeight, this._nodeUnderlay.setHeight, nodeUnderlayHeight);
    }
  }

  if (playable) {
    dataAnimHandler.add(playable, DvtAdfDiagramDataAnimationHandler.PHASE_UPDATE);
  }
};

dvt.AdfDiagramNode.prototype.AnimateUpdateDisclosure = function(dataAnimHandler, oldNode, anim, newTx, newTy) {
  var thisRef = this;

  //Bug 15839721: temporarily prevent mouseover from triggering
  //a new hover effect until after the animation is done, because
  //it will interrupt the animation and cause the underlay size
  //to appear to jump
  if (this._nodeUnderlay) {
    this._bPreventHoverEffect = true;
    var hoverEffectEnaFunc = function() {
      thisRef._bPreventHoverEffect = false;
    };
    dvt.Playable.appendOnEnd(anim, hoverEffectEnaFunc);
  }

  //add the old node just in front of this node
  var parent = this.getParent();
  var childIndex = parent.getChildIndex(this);
  parent.addChildAt(oldNode, childIndex + 1);
  //need to translate the old node in sync with the new node while it fades
  anim.addProp(dvt.Animator.TYPE_NUMBER, oldNode, oldNode.getTranslateX, oldNode.setTranslateX, newTx);
  anim.addProp(dvt.Animator.TYPE_NUMBER, oldNode, oldNode.getTranslateY, oldNode.setTranslateY, newTy);

  anim.addProp(dvt.Animator.TYPE_NUMBER, oldNode, oldNode.getAlpha, oldNode.setAlpha, 0);
  dvt.Playable.appendOnEnd(anim, function() {
    oldNode.getParent().removeChild(oldNode);
    oldNode.destroy();
  });
  var nodeContent = this._getContentContainer() ? this._getContentContainer() : this;
  nodeContent.setAlpha(0);
  anim.addProp(dvt.Animator.TYPE_NUMBER, nodeContent, nodeContent.getAlpha, nodeContent.setAlpha, this.GetMaxAlpha());

  //scale the disclosed node to shrink to or grow from the size of the
  //undisclosed node
  var oldContentBounds = oldNode.getContentBounds();
  var newContentBounds = this.getContentBounds();
  if (oldNode.isDisclosed()) {
    anim.addProp(dvt.Animator.TYPE_NUMBER, oldNode, oldNode.getScaleX, oldNode.setScaleX, newContentBounds.w / oldContentBounds.w);
    anim.addProp(dvt.Animator.TYPE_NUMBER, oldNode, oldNode.getScaleY, oldNode.setScaleY, newContentBounds.h / oldContentBounds.h);

    //hide the old children during delete phase
    var deletePlayable = this.CreateCustomAnimPlayable();
    deletePlayable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, oldNode._childNodePane, oldNode._childNodePane.getAlpha, oldNode._childNodePane.setAlpha, 0);
    dataAnimHandler.add(deletePlayable, DvtAdfDiagramDataAnimationHandler.PHASE_DELETE);
  }
  else if (this.isDisclosed()) {
    this.setScale(oldContentBounds.w / newContentBounds.w, oldContentBounds.h / newContentBounds.h);
    anim.addProp(dvt.Animator.TYPE_NUMBER, this, this.getScaleX, this.setScaleX, 1);
    anim.addProp(dvt.Animator.TYPE_NUMBER, this, this.getScaleY, this.setScaleY, 1);

    //show the new children during insert phase
    var insertPlayable = this.CreateCustomAnimPlayable();
    this._childNodePane.setAlpha(0);
    insertPlayable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, this._childNodePane, this._childNodePane.getAlpha, this._childNodePane.setAlpha, 1);
    dataAnimHandler.add(insertPlayable, DvtAdfDiagramDataAnimationHandler.PHASE_INSERT);
  }
};


/**
 * Animates any updates to hidden ancestors
 *
 * @param {dvt.DataAnimationHandler} dataAnimHandler the dvt.DataAnimationHandler that is managing the current animator
 * @param {dvt.Animator} anim the animator into which the updates should be rendered
 */
dvt.AdfDiagramNode.prototype.AnimateUpdateHiddenAncestor = function(dataAnimHandler, anim) {
  var alpha = this.isHiddenAncestor() ? 0 : 1;
  var bMouseEnabled = !this.isHiddenAncestor();

  if (this._stackUnderlay) {
    this._stackUnderlay.setAlpha(1 - alpha);
    anim.addProp(dvt.Animator.TYPE_NUMBER, this._stackUnderlay, this._stackUnderlay.getAlpha, this._stackUnderlay.setAlpha, alpha);
  }
  if (this._stackBadge) {
    this._stackBadge.setAlpha(1 - alpha);
    anim.addProp(dvt.Animator.TYPE_NUMBER, this._stackBadge, this._stackBadge.getAlpha, this._stackBadge.setAlpha, alpha);
  }
  if (this._afRoot) {
    this._afRoot.setMouseEnabled(bMouseEnabled);
    this._afRoot.setAlpha(1 - alpha);
    anim.addProp(dvt.Animator.TYPE_NUMBER, this._afRoot, this._afRoot.getAlpha, this._afRoot.setAlpha, alpha);
  }
  if (this._containerBadge) {
    this._containerBadge.setMouseEnabled(bMouseEnabled);
    this._containerBadge.setAlpha(1 - alpha);
    anim.addProp(dvt.Animator.TYPE_NUMBER, this._containerBadge, this._containerBadge.getAlpha, this._containerBadge.setAlpha, alpha);
  }
  if (this._partialExpandIndicator) {
    this._partialExpandIndicator.setMouseEnabled(bMouseEnabled);
    this._partialExpandIndicator.setAlpha(1 - alpha);
    anim.addProp(dvt.Animator.TYPE_NUMBER, this._partialExpandIndicator, this._partialExpandIndicator.getAlpha, this._partialExpandIndicator.setAlpha, alpha);
  }
  if (this._containerChrome) {
    //Bug 15838130: disable mouse events on container chrome when
    //container is drill ancestor
    this._containerChrome.setMouseEnabled(bMouseEnabled);
    this._containerChrome.setAlpha(1 - alpha);
    anim.addProp(dvt.Animator.TYPE_NUMBER, this._containerChrome, this._containerChrome.getAlpha, this._containerChrome.setAlpha, alpha);
  }
  if (this._labelObj) {
    this._labelObj.setMouseEnabled(bMouseEnabled);
    this._labelObj.setAlpha(1 - alpha);
    anim.addProp(dvt.Animator.TYPE_NUMBER, this._labelObj, this._labelObj.getAlpha, this._labelObj.setAlpha, alpha);
  }
  if (this._overlays) {
    for (var iOverlay = 0; iOverlay < this._overlays.length; iOverlay++) {
      var overlay = this._overlays[iOverlay]['overlay'];
      overlay.setMouseEnabled(bMouseEnabled);
      overlay.setAlpha(1 - alpha);
      anim.addProp(dvt.Animator.TYPE_NUMBER, overlay, overlay.getAlpha, overlay.setAlpha, alpha);
    }
  }
  //Bug 15838130: hide node underlay when container is drill ancestor
  if (this._nodeUnderlay) {
    this._nodeUnderlay.setAlpha(1 - alpha);
    anim.addProp(dvt.Animator.TYPE_NUMBER, this._nodeUnderlay, this._nodeUnderlay.getAlpha, this._nodeUnderlay.setAlpha, alpha);
  }
};

/**
 * @protected
 * Animates insertion of a diagram node
 * @param {DvtAdfDiagramDataAminationHandler} dataAnimHandler daigram data animation handler
 */
dvt.AdfDiagramNode.prototype.AnimateInsert = function(dataAnimHandler) {
  var bAnim = true;
  var groupId = this.getData().getGroupId();
  if (groupId) {
    var groupNode = this.GetDiagram()._oldNodes[groupId];
    //if the group didn't exist in the old diagram, then this node
    //is a descendant of a group being inserted, so no need to
    //animate it separately because the top level inserted group
    //will handle it
    if (!groupNode) {
      bAnim = false;
    }
  }
  if (bAnim) {
    var playable = new dvt.CustomAnimation(this.getCtx(), this.GetDiagram().getAnimationDuration());
    var anim = playable.getAnimator();
    this.setAlpha(0);
    anim.addProp(dvt.Animator.TYPE_NUMBER, this, this.getAlpha, this.setAlpha, this._maxAlpha);
    dataAnimHandler.add(playable, DvtAdfDiagramDataAnimationHandler.PHASE_INSERT);
    //don't process the descendants separately
    dataAnimHandler.SkipDescendants(this.getData().getId(), this.GetDiagram());
  }
};

/**
 * @protected
 * Animates deletion of a diagram node
 * @param {DvtAdfDiagramDataAminationHandler} dataAnimHandler daigram data animation handler
 * @param {dvt.AdfDiagram} newDiagram new diagram
 */
dvt.AdfDiagramNode.prototype.AnimateDelete = function(dataAnimHandler, newDiagram) {
  var bAnim = true;
  var groupId = this.getData().getGroupId();
  if (groupId) {
    var groupNode = newDiagram.getNodeById(groupId);
    //if the group node exists in the new diagram, then reparent
    //this node to it and animate it
    if (groupNode && groupNode.GetChildNodePane()) {
      this.InsertNodeIntoParent(groupNode.GetChildNodePane());
    }
    //if the group node doesn't exist in the new diagram, then
    //this node must be a descendant of a group being deleted, so
    //don't need to reparent or animate this node separately because
    //the top level group being deleted will take care of it
    else {
      bAnim = false;
    }
  }
  else {
    //if not in a group, then reparent this node and animate it
    this.InsertNodeIntoParent(newDiagram._nodesPane);
  }
  if (bAnim) {
    var playable = new dvt.CustomAnimation(this.getCtx(), newDiagram.getAnimationDuration());
    var anim = playable.getAnimator();
    anim.addProp(dvt.Animator.TYPE_NUMBER, this, this.getAlpha, this.setAlpha, 0);
    var thisRef = this;
    var removeFunc = function() {
      thisRef.getParent().removeChild(thisRef);
      thisRef.destroy();
    };
    dvt.Playable.appendOnEnd(playable, removeFunc);
    dataAnimHandler.add(playable, DvtAdfDiagramDataAnimationHandler.PHASE_DELETE);
    //don't process the descendants separately
    dataAnimHandler.SkipDescendants(this.getData().getId(), this.GetDiagram());
  }
};

dvt.AdfDiagramNode.prototype.isRestackable = function() {
  var bRestackable = false;
  if (this.getData().isShowStack() && !this.isStack()) {
    var attrGrps = this._diagram.getAttributeGroups();
    var explodedStacks = this._diagram.getExplodedStacks();
    if (attrGrps && attrGrps.length > 0 && explodedStacks && explodedStacks.length > 0) {
      //can't use this.GetStackId() because this node is not actually
      //in a stack at the moment
      var stackId = this._diagram.CreateStackName(this.getData().getId(), this._diagram.getAttributeGroups());
      for (var iStack = 0; iStack < explodedStacks.length; iStack++) {
        if (stackId == explodedStacks[iStack]) {
          bRestackable = true;
          break;
        }
      }
    }
  }
  return bRestackable;
};

dvt.AdfDiagramNode.prototype.getDetailContents = function()
{
  return this._detailContents;
};

dvt.AdfDiagramNode.prototype.getTabStopsArray = function() {
  return this._tabStopsNodeArray;
};


/**
 * Get next object for the tab stop
 * @return {object} next tab stop
 */
dvt.AdfDiagramNode.prototype.getNextTabStop = function() {

  this._curTabStopIdx++;

  if (this._tabStopsNodeArray && this._tabStopsNodeArray.length > this._curTabStopIdx) {
    return this._tabStopsNodeArray[this._curTabStopIdx];
  }
  else {
    this._curTabStopIdx = -1;
    return null;
  }
};


/**
 * Get previous object for the tab stop
 * @return {object} previous tab stop
 */
dvt.AdfDiagramNode.prototype.getPrevTabStop = function() {

  this._curTabStopIdx--;

  if (this._curTabStopIdx >= 0 && this._tabStopsNodeArray && this._tabStopsNodeArray.length > this._curTabStopIdx)
    return this._tabStopsNodeArray[this._curTabStopIdx];
  else {
    this._curTabStopIdx = -1;
    return null;
  }
};


/**
 * Get current object for the tab stop
 * @return {object} current tab stop
 */
dvt.AdfDiagramNode.prototype.getCurrentTabStop = function() {

  if (this._curTabStopIdx > -1 && this._tabStopsNodeArray && this._tabStopsNodeArray.length > this._curTabStopIdx)
    return this._tabStopsNodeArray[this._curTabStopIdx];
  else
    return null;
};


/**
 * Reset tab stop array - clear the keyboard focus and reset index for the current focus
 */
dvt.AdfDiagramNode.prototype.resetTabStops = function() {
  var focusElement = this.getCurrentTabStop();
  if (focusElement && focusElement != this && focusElement.hideKeyboardFocusEffect)
    focusElement.hideKeyboardFocusEffect();
  this._curTabStopIdx = -1;
};


/**
 * @protected
 * Helper method that creates a link handle for the node. The link handle is used to create a new link from the node.
 * @return {DvtAdfDiagramNodeEndpoint} link handle for the node
 */
dvt.AdfDiagramNode.prototype.CreateLinkHandle = function() {
  var palette = this._diagram.getPalette();
  if (palette && !palette.hasLinkSection()) {
    return null;
  }
  var linkHandle = new DvtAdfDiagramNodeEndpoint(this.getCtx(), this, this.getId(), this._diagram.getEditorStyles());

  var nodeBounds = this.getContentBounds();
  linkHandle.setPosition(new dvt.Point(nodeBounds.w / 2, 0));
  linkHandle.setCursor(dvt.SelectionEffectUtils.getSelectingCursor());
  linkHandle.setVisible(false);
  this.addChild(linkHandle);
  return linkHandle;
};


/**
 * Show link handle for the node
 */
dvt.AdfDiagramNode.prototype.showLinkHandle = function() {
  this._linkHandle = this.getLinkHandle();
  if (this._linkHandle && !this._diagram.isDragged())
    this._linkHandle.setVisible(true);
};


/**
 * Hide link handle
 */
dvt.AdfDiagramNode.prototype.hideLinkHandle = function() {
  if (this._linkHandle)
    this._linkHandle.setVisible(false);
};


/**
 * Get link handle for the node
 * @return {DvtAdfDiagramNodeEndpoint} link handle for the node
 */
dvt.AdfDiagramNode.prototype.getLinkHandle = function() {
  if (!this._diagram.isReadOnly() && this._linkHandle == null)
    this._linkHandle = this.CreateLinkHandle();
  return this._linkHandle;
};


dvt.AdfDiagramNode.prototype.getForceZoom100Facet = function()
{
  return this._bForceZoom100Facet;
};

/**
 * Get the zoom level of the node
 * @return {number} zoom level
 */
dvt.AdfDiagramNode.prototype.getZoom = function()
{
  return this._zoom;
};

/**
 * Set the zoom level of the node
 * Fix Bug 22121707 - hv: zoom facet doesn't change when zoom in
 * Note: Zoom level is based on the chosen template in dvt.AdfDiagramNode.GetNodeTemplate
 * This function should not be used to change zoom level and will have no effect on node / node template
 * This function can only be used to restore the zoom level modified by forceZoom100 facet
 *
 * @param {number} zoom zoom level
 */
dvt.AdfDiagramNode.prototype.setZoom = function(zoom) {
  this._zoom = zoom;
};

dvt.AdfDiagramNode.prototype.ClearNodeSize = function()
{
  this._nodeWidth100 = 0;
  this._nodeHeight100 = 0;
  this._nodeWidth = 0;
  this._nodeHeight = 0;
};


/**
 * Sets the externally specified width for this node
 *
 * @param {number} w the width
 */
dvt.AdfDiagramNode.prototype.setWidth = function(w) {
  this._width = w;
};


/**
 * Gets the externally specified width for this node
 *
 * @return {number} the width
 */
dvt.AdfDiagramNode.prototype.getWidth = function() {
  return this._width;
};


/**
 * Sets the externally specified height for this node
 *
 * @param {number} h the height
 */
dvt.AdfDiagramNode.prototype.setHeight = function(h) {
  this._height = h;
};


/**
 * Gets the externally specified height for this node
 *
 * @return {number} the height
 */
dvt.AdfDiagramNode.prototype.getHeight = function() {
  return this._height;
};


/**
 * Removes a temporary copy of the node content, that was stored to finish zoom operation on touch
 */
dvt.AdfDiagramNode.prototype.cleanUp = function()
{
  if (this._copyForDelayedRemoval) {
    this.removeChild(this._copyForDelayedRemoval);
    var childCount = this._copyForDelayedRemoval.getNumChildren();
    for (var i = 0; i < childCount; i++) {
      var child = this._copyForDelayedRemoval.getChildAt(i);
      this._copyForDelayedRemoval.removeChild(child);
      if (child && child.destroy)
        child.destroy();
    }
  }
  this._copyForDelayedRemoval = null;
};


/**
 * Helper function that creates a copy of the node content, that will be released later when the content is not needed.
 * The content is stored in the container which is attached to the node and the visibility of the container is false.
 * @protected
 * @return {dvt.Container} container attached to the current node. The container containes child objects that belong to the old node content.
 */
dvt.AdfDiagramNode.prototype.CreateCopyForDelayedRemoval = function()
{
  var container = new dvt.Container(this.getCtx(), this.getData().getId() + '_tempCopy');
  if (this._afRoot)
    container.addChild(this._afRoot);
  this._afRoot = null;
  if (this._labelObj)
    container.addChild(this._labelObj);
  this._labelObj = null;
  if (this._backgroundObj)
    container.addChild(this._backgroundObj);
  this._backgroundObj = null;
  if (this._overlays) {
    for (var iOv = 0; iOv < this._overlays.length; iOv++)
    {
      var overlay = this._overlays[iOv]['overlay'];
      container.addChild(overlay);
    }
    this._overlays = null;
  }
  if (this._containerTopObj)
    container.addChild(this._containerTopObj);
  this._containerTopObj = null;
  if (this._containerBottomObj)
    container.addChild(this._containerBottomObj);
  this._containerBottomObj = null;
  if (this._containerLeftObj)
    container.addChild(this._containerLeftObj);
  this._containerLeftObj = null;
  if (this._containerRightObj)
    container.addChild(this._containerRightObj);
  this._containerRightObj = null;
  if (this._containerChrome)
    container.addChild(this._containerChrome);
  this._containerChrome = null;
  if (this._containerUnderlay)
    container.addChild(this._containerUnderlay);
  this._containerUnderlay = null;
  if (this._focusUnderlay)
    container.addChild(this._focusUnderlay);
  this._focusUnderlay = null;

  container.setVisible(false);
  this.addChild(container);
  return container;
};


/**
 * Checks whether we can safely remove content of a node.
 * The content can not be removed during zoom operation on touch device when the target (child of the node) is cached by TouchManager
 * on "touchstart", but the target is not cleared yet by "touchend".
 * @private
 * @return {boolean} true if the content can be removed
 */
dvt.AdfDiagramNode.prototype._canRemoveNodeContent = function() {
  var res = true;
  if (dvt.Agent.isTouchDevice()) {
    var targets = this.GetDiagram().getPanZoomCanvas().getCurrentTouchTargets();
    if (targets) {
      for (var i = 0; i < targets.length; i++) {
        var isAncestor = dvt.DisplayableUtils.isAncestor(this, targets[i]);
        if (isAncestor)
          res = false;
      }
    }
  }
  return res;
};


/**
 * Sets the draggable attribute for this node
 *
 * @param {boolean} draggable True if the node is draggable
 */
dvt.AdfDiagramNode.prototype.setDraggable = function(draggable)
{
  this._draggable = draggable;
};


/**
 * Gets the draggable attribute for this node
 *
 * @return {boolean} True if the node is draggable (the default)
 */
dvt.AdfDiagramNode.prototype.isDraggable = function()
{
  return this._draggable;
};

/**
 * Sets drag flavor for the node
 * @param {string} dragFlavor drag flavor for the node
 */
dvt.AdfDiagramNode.prototype.setDragFlavor = function(dragFlavor)
{
  this.DragFlavor = dragFlavor && this._diagram.ConvertDataFlavorFunc ?
      this._diagram.ConvertDataFlavorFunc('javax.faces.component.UIComponent/' + dragFlavor) : null;
};

/**
 * Sets drop flavors for the node
 * @param {string} dropFlavors space separated data flavors for the diagram node
 */
dvt.AdfDiagramNode.prototype.setDropFlavors = function(dropFlavors)
{
  this.DropFlavors = dvt.AdfDiagramJsonParser.ParseDropFlavors(dropFlavors, this._diagram.ConvertDataFlavorFunc);
};

/**
 * Returns an object containing the drag context info for the node
 * @param {number} mouseX
 * @param {number} mouseY
 * @return {object} a map containing the data flavor and object type (to be wrapped by the peer)
 */
dvt.AdfDiagramNode.prototype.getDragContext = function(mouseX, mouseY) {
  return {'dragFlavor': this.DragFlavor, 'typeName': this.getTypeName()};
};

/**
 * Sets the cursor for this node
 * @private
 */
dvt.AdfDiagramNode.prototype._refreshCursor = function() {
  var selectingCursor = dvt.SelectionEffectUtils.getSelectingCursor();
  if (!this.isHiddenAncestor()) {
    if (this.isSelectable()) {
      this.setCursor(selectingCursor);
      return;
    }
    var spbs = this.getShowPopupBehaviors();
    if (dvt.ArrayUtils.isArray(spbs)) {
      for (var i = 0; i < spbs.length; i++) {
        if (spbs[i] && spbs[i].getTriggerType() == 'click') {
          this.setCursor(selectingCursor);
          return;
        }
      }
    }
    var cbs = this.getClientBehaviors();
    if (dvt.ArrayUtils.isArray(cbs)) {
      for (var i = 0; i < cbs.length; i++) {
        if (cbs[i] && cbs[i].getTriggerType() == 'click') {
          this.setCursor(selectingCursor);
          return;
        }
      }
    }
  }
  this.setCursor(null);
};


/**
 * @private
 * Creates a fixed width stroke for stack underlay
 * @param {String} sc  A css color specification for the stroke color.
 * @param {String} sa  An optional stroke alpha.
 * @param {number} sw  An optioanl width of the stroke line.  If omitted, the default width is 1.
 */
dvt.AdfDiagramNode.prototype._getUnderlayStroke = function(sc, sa, sw) {
  var scale = this._diagram.supportsVectorEffects() ? 1 : 1 / this._diagram.getPanZoomCanvas().getZoom();
  var stroke = new dvt.SolidStroke(sc, sa ? sa : 1, (sw ? sw : 1) * scale);
  stroke.setFixedWidth(true);
  return stroke;
};

/**
 * @protected
 * Gets child node pane for the diagram node
 * @return {dvt.Container} child node pane for the diagram node
 */
dvt.AdfDiagramNode.prototype.GetChildNodePane = function() {
  return this._childNodePane;
};

/**
 * @override
 */
dvt.AdfDiagramNode.prototype.SetPosition = function(xx, yy, animator) {
  if (animator) {
    animator.addProp(dvt.Animator.TYPE_NUMBER, this, this.getTranslateX, this.setTranslateX, xx);
    animator.addProp(dvt.Animator.TYPE_NUMBER, this, this.getTranslateY, this.setTranslateY, yy);
  }
  else {
    this.setTranslate(xx, yy);
  }
  if (! (this.getParent() instanceof DvtAdfDiagramLayoutContainer)) {
    DvtAdfDiagramOverviewUtils.SetNodePosition(this.GetDiagram(), this, xx, yy, animator);
  }
};

/**
 * Sets alpha for the node
 * @param {number} alpha for the node
 * @param {dvt.Animator} animator The animator into which the transitions should be rendered
 * @protected
 */
dvt.AdfDiagramNode.prototype.SetAlpha = function(alpha, animator) 
{
  var displayables = this.getDisplayables();

  if (animator) {
    for (var i = 0; i < displayables.length; i++) {
      animator.addProp(dvt.Animator.TYPE_NUMBER, displayables[i],
                       displayables[i].getAlpha, displayables[i].setAlpha, alpha);
    }
  }
  else {
    for (var i = 0; i < displayables.length; i++) {
      displayables[i].setAlpha(alpha);
    }
  }
  DvtAdfDiagramOverviewUtils.SetNodeAlpha(this.GetDiagram(), this, alpha, animator);
};

/**
 * Get the automation state of the node for the passed state key
 * This will return pre-transition state value until the node animation is complete.
 * @param {string} state   State key
 * @return {boolean}  state value of the diagram node
 */
dvt.AdfDiagramNode.prototype.getAutomationState = function(state) {
  var value = this.GetDiagram().getAutomationState(this.getData().getId(), state);
  if (value == null) {
    if (state == 'stack')
      return this.isStack();
    if (state == 'disclosed')
      return this.isDisclosed();
    if (state == 'partialExpand')
      return this.isPartiallyExpanded();
    if (state == 'visible')
      return this.getVisible();
  }
  return value;
};

/**
 * @private
 * Creates a structure to store container facet tab stops
 * @param {dvt.AfContext} afContext the rendering context for the af components
 * @param {string} facetName facet name
 */
dvt.AdfDiagramNode.prototype._addContainerFacetTabStops = function(afContext, facetName) {
  if (!this._containerFacetsTabStops) {
    this._containerFacetsTabStops = {};
  }
  this._containerFacetsTabStops[facetName] = [];
  afContext.setTabStopsArray(this._containerFacetsTabStops[facetName]);
};

/**
 * @private
 * Removes tab stops associated with container facets from tab stops array for the node
 */
dvt.AdfDiagramNode.prototype._clearContainerFacetTabStops = function() {
  if (!(this._containerFacetsTabStops && this._tabStopsNodeArray)) {
    return;
  }
  var clearStops = function(source, target) {
    if (!source)
      return;
    var itemIdx = dvt.ArrayUtils.getIndex(target, source[0]);
    if (itemIdx > 0)
      target.splice(itemIdx, source.length);
  };
  clearStops(this._containerFacetsTabStops['top'], this._tabStopsNodeArray);
  clearStops(this._containerFacetsTabStops['left'], this._tabStopsNodeArray);
  clearStops(this._containerFacetsTabStops['right'], this._tabStopsNodeArray);
  clearStops(this._containerFacetsTabStops['bottom'], this._tabStopsNodeArray);
  this._containerFacetsTabStops = {};
};

/**
 * @private
 * Adds container facet tab stops to the tab stops array for the node
 */
dvt.AdfDiagramNode.prototype._updateNodeTabStops = function() {
  if (!(this._containerFacetsTabStops && this._tabStopsNodeArray)) {
    return;
  }
  var addStops = function(source, target) {
    if (!source)
      return;
    for (var idx = 0; idx < source.length; idx++)
      target.push(source[idx]);
  };

  if (this._tabStopsNodeArray.length > 1 && this._tabStopsNodeArray[this._tabStopsNodeArray.length - 1] == this)
    this._tabStopsNodeArray.pop();
  addStops(this._containerFacetsTabStops['top'], this._tabStopsNodeArray);
  if (dvt.Agent.isRightToLeft()) {
    addStops(this._containerFacetsTabStops['right'], this._tabStopsNodeArray);
    addStops(this._containerFacetsTabStops['left'], this._tabStopsNodeArray);
  }
  else {
    addStops(this._containerFacetsTabStops['left'], this._tabStopsNodeArray);
    addStops(this._containerFacetsTabStops['right'], this._tabStopsNodeArray);
  }
  addStops(this._containerFacetsTabStops['bottom'], this._tabStopsNodeArray);
};

/**
 * @override
 */
dvt.AdfDiagramNode.prototype.getGroupId = function() {
  return this.getData().getGroupId();
};
/**
 * @constructor
 */
dvt.AdfDiagramNodeDef = function()
{
  this.Init();
};

dvt.Obj.createSubclass(dvt.AdfDiagramNodeDef, dvt.Obj, 'dvt.AdfDiagramNodeDef');


/**
 * Initialization method called by the constructor
 *
 * @protected
 */
dvt.AdfDiagramNodeDef.prototype.Init = function() {
  this._id = null;
  this._inlineStyle = null;
  this._skinStyle = null;
  this._styleClass = null;
  this._nodeTemplates = {};

  this._propMap = null;
  this._detailTemplate = null;
  this._labelTemplate = null;
  this._backgroundTemplate = null;
  this._containerTopTemplate = null;
  this._containerBottomTemplate = null;
  this._containerLeftTemplate = null;
  this._containerRightTemplate = null;
  this._overlayTemplate = null;

  this._showPopupBehaviors = null;
  this._clientBehaviors = null;
};

dvt.AdfDiagramNodeDef.prototype.setId = function(id)
{
  this._id = id;
};

dvt.AdfDiagramNodeDef.prototype.getId = function()
{
  return this._id;
};


/**
 * Sets the width for this node definition
 *
 * @param {string} ww the width (may be a literal value or an expression)
 */
dvt.AdfDiagramNodeDef.prototype.setWidth = function(ww) {
  this._setPropMapProperty(dvt.AdfDiagramParser._ATTR_WIDTH, ww);
};


/**
 * Gets the width for this node definition
 *
 * @return {string} the width (may be a literal value or an expression)
 */
dvt.AdfDiagramNodeDef.prototype.getWidth = function() {
  return this._getPropMapProperty(dvt.AdfDiagramParser._ATTR_WIDTH);
};


/**
 * Sets the height for this node definition
 *
 * @param {string} hh the height (may be a literal value or an expression)
 */
dvt.AdfDiagramNodeDef.prototype.setHeight = function(hh) {
  this._setPropMapProperty(dvt.AdfDiagramParser._ATTR_HEIGHT, hh);
};


/**
 * Gets the height for this node definition
 *
 * @return {string} the height (may be a literal value or an expression)
 */
dvt.AdfDiagramNodeDef.prototype.getHeight = function() {
  return this._getPropMapProperty(dvt.AdfDiagramParser._ATTR_HEIGHT);
};

dvt.AdfDiagramNodeDef.prototype.setInlineStyle = function(inlineStyle)
{
  this._inlineStyle = inlineStyle;
};

dvt.AdfDiagramNodeDef.prototype.getInlineStyle = function()
{
  return this._inlineStyle;
};

dvt.AdfDiagramNodeDef.prototype.setSkinStyle = function(skinStyle)
{
  this._skinStyle = skinStyle;
};

dvt.AdfDiagramNodeDef.prototype.getSkinStyle = function()
{
  return this._skinStyle;
};

dvt.AdfDiagramNodeDef.prototype.setStyleClass = function(styleClass)
{
  this._styleClass = styleClass;
};

dvt.AdfDiagramNodeDef.prototype.getStyleClass = function()
{
  return this._styleClass;
};


dvt.AdfDiagramNodeDef.prototype.setNodeTemplate = function(template, zoomFacet)
{
  this._nodeTemplates[zoomFacet] = template;
};

dvt.AdfDiagramNodeDef.prototype.getNodeTemplate = function(zoomFacet)
{
  var zf = zoomFacet;
  if (!zf)
  {
    zf = 'zoom100';
  }
  return this._nodeTemplates[zf];
};

dvt.AdfDiagramNodeDef.prototype.setDetailTemplate = function(template)
{
  this._detailTemplate = template;
};

dvt.AdfDiagramNodeDef.prototype.getDetailTemplate = function()
{
  return this._detailTemplate;
};

dvt.AdfDiagramNodeDef.prototype.setLabelTemplate = function(template)
{
  this._labelTemplate = template;
};

dvt.AdfDiagramNodeDef.prototype.getLabelTemplate = function()
{
  return this._labelTemplate;
};

dvt.AdfDiagramNodeDef.prototype.setBackgroundTemplate = function(template)
{
  this._backgroundTemplate = template;
};

dvt.AdfDiagramNodeDef.prototype.getBackgroundTemplate = function()
{
  return this._backgroundTemplate;
};

dvt.AdfDiagramNodeDef.prototype.setContainerTopTemplate = function(template)
{
  if (template instanceof dvt.AfPanelGroupLayout) {
    // stretch width
    template.setInlineStyle(this._prependInlineStyle('width:100%', template.getInlineStyle()));
  }
  this._containerTopTemplate = template;
};

dvt.AdfDiagramNodeDef.prototype.getContainerTopTemplate = function()
{
  return this._containerTopTemplate;
};

dvt.AdfDiagramNodeDef.prototype.setContainerBottomTemplate = function(template)
{
  if (template instanceof dvt.AfPanelGroupLayout) {
    // stretch width
    template.setInlineStyle(this._prependInlineStyle('width:100%', template.getInlineStyle()));
  }
  this._containerBottomTemplate = template;
};

dvt.AdfDiagramNodeDef.prototype.getContainerBottomTemplate = function()
{
  return this._containerBottomTemplate;
};

dvt.AdfDiagramNodeDef.prototype.setContainerLeftTemplate = function(template)
{
  if (template instanceof dvt.AfPanelGroupLayout) {
    // stretch height
    template.setInlineStyle(this._prependInlineStyle('height:100%', template.getInlineStyle()));
  }
  this._containerLeftTemplate = template;
};

dvt.AdfDiagramNodeDef.prototype.getContainerLeftTemplate = function()
{
  return this._containerLeftTemplate;
};

dvt.AdfDiagramNodeDef.prototype.setContainerRightTemplate = function(template)
{
  if (template instanceof dvt.AfPanelGroupLayout) {
    // stretch height
    template.setInlineStyle(this._prependInlineStyle('height:100%', template.getInlineStyle()));
  }
  this._containerRightTemplate = template;
};

dvt.AdfDiagramNodeDef.prototype.getContainerRightTemplate = function()
{
  return this._containerRightTemplate;
};

dvt.AdfDiagramNodeDef.prototype.setOverlayTemplate = function(template)
{
  this._overlayTemplate = template;
};

dvt.AdfDiagramNodeDef.prototype.getOverlayTemplate = function()
{
  return this._overlayTemplate;
};

dvt.AdfDiagramNodeDef.prototype.setShortDesc = function(shortDesc)
{
  this._setPropMapProperty(dvt.AdfDiagramParser._ATTR_AF_SHORT_DESC, shortDesc);
};

dvt.AdfDiagramNodeDef.prototype.getShortDesc = function()
{
  return this._getPropMapProperty(dvt.AdfDiagramParser._ATTR_AF_SHORT_DESC);
};

dvt.AdfDiagramNodeDef.prototype.setLayout = function(layout)
{
  this._setPropMapProperty(dvt.AdfDiagramParser._ATTR_LAYOUT, layout);
};

dvt.AdfDiagramNodeDef.prototype.getLayout = function()
{
  return this._getPropMapProperty(dvt.AdfDiagramParser._ATTR_LAYOUT);
};

dvt.AdfDiagramNodeDef.prototype.setReadOnly = function(readOnly)
{
  this._setPropMapProperty(dvt.AdfDiagramParser._ATTR_AF_READ_ONLY, readOnly);
};

dvt.AdfDiagramNodeDef.prototype.getReadOnly = function()
{
  return this._getPropMapProperty(dvt.AdfDiagramParser._ATTR_AF_READ_ONLY);
};

dvt.AdfDiagramNodeDef.prototype.setSelectable = function(selectable)
{
  this._setPropMapProperty(dvt.AdfDiagramParser._ATTR_SELECTABLE, selectable);
};

dvt.AdfDiagramNodeDef.prototype.getSelectable = function()
{
  return this._getPropMapProperty(dvt.AdfDiagramParser._ATTR_SELECTABLE);
};

dvt.AdfDiagramNodeDef.prototype.setLabel = function(label)
{
  this._setPropMapProperty(dvt.AdfDiagramParser._ATTR_LABEL, label);
};

dvt.AdfDiagramNodeDef.prototype.getLabel = function()
{
  return this._getPropMapProperty(dvt.AdfDiagramParser._ATTR_LABEL);
};


/**
 * Sets the draggable attribute for this node definition
 *
 * @param {string} draggable specifies if a node is draggable. Valid values are 'on'/'off', 'yes'/'no'(deprecated) or EL.
 */
dvt.AdfDiagramNodeDef.prototype.setDraggable = function(draggable)
{
  this._setPropMapProperty(dvt.AdfDiagramParser._ATTR_AF_DRAGGABLE, draggable);
};


/**
 * Gets the draggable attribute for this node definition
 *
 * @return {string} specifies if a node is draggable. Valid values are 'on'/'off', 'yes'/'no'(deprecated) or EL
 */
dvt.AdfDiagramNodeDef.prototype.getDraggable = function()
{
  return this._getPropMapProperty(dvt.AdfDiagramParser._ATTR_AF_DRAGGABLE);
};

/**
 * Sets drag flavor for the node definition
 * @param {string} dragFlavor drag flavor or EL
 */
dvt.AdfDiagramNodeDef.prototype.setDragFlavor = function(dragFlavor)
{
  this._setPropMapProperty(dvt.AdfDiagramParser._ATTR_AF_DRAG_FLAVOR, dragFlavor);
};


/**
 * Gets drag flavor for the node definition
 * @return {string} drag flavor or EL
 */
dvt.AdfDiagramNodeDef.prototype.getDragFlavor = function()
{
  return this._getPropMapProperty(dvt.AdfDiagramParser._ATTR_AF_DRAG_FLAVOR);
};

/**
 * Sets drop flavors for the node definition
 * @param {string} dropFlavors space separated string of data flavors or EL
 */
dvt.AdfDiagramNodeDef.prototype.setDropFlavors = function(dropFlavors)
{
  this._setPropMapProperty(dvt.AdfDiagramParser._ATTR_AF_DROP_FLAVORS, dropFlavors);
};


/**
 * Get drop flavors for the node definition
 * @return {string} space separated string of data flavors or EL
 */
dvt.AdfDiagramNodeDef.prototype.getDropFlavors = function()
{
  return this._getPropMapProperty(dvt.AdfDiagramParser._ATTR_AF_DROP_FLAVORS);
};


/**
 * Stamps this template out on the specified node based on the specified data attributes
 *
 * @param {object} elContext a map containing the data attributes for the node
 * @param {dvt.AdfDiagramNode} node the node to be stamped out
 */
dvt.AdfDiagramNodeDef.prototype.stamp = function(elContext, node)
{
  if (this._propMap)
  {
    var resolvedMap = this._propMap.stamp(elContext);
    this.ProcessResolvedProperties(resolvedMap, node);
  }
};

/**
 * Resolves the Properties
 * @protected
 * @param {dvt.PropMap} resolvedMap a map containing the resolved properties for the node
 * @param {dvt.AdfDiagramNode} node the node to be stamped out
 */
dvt.AdfDiagramNodeDef.prototype.ProcessResolvedProperties = function(resolvedMap, node) {
  node.setWidth(resolvedMap.getProperty(dvt.AdfDiagramParser._ATTR_WIDTH) ? parseInt(resolvedMap.getProperty(dvt.AdfDiagramParser._ATTR_WIDTH)) : null);
  node.setHeight(resolvedMap.getProperty(dvt.AdfDiagramParser._ATTR_HEIGHT) ? parseInt(resolvedMap.getProperty(dvt.AdfDiagramParser._ATTR_HEIGHT)) : null);
  node.setShortDesc(resolvedMap.getProperty(dvt.AdfDiagramParser._ATTR_AF_SHORT_DESC));
  node.setLayout(resolvedMap.getProperty(dvt.AdfDiagramParser._ATTR_LAYOUT));
  node.setLabel(resolvedMap.getProperty(dvt.AdfDiagramParser._ATTR_LABEL));

  node.setDeleteEnabled(!resolvedMap.getBooleanProp(dvt.AdfDiagramParser._ATTR_AF_READ_ONLY, false));
  node.setSelectable(resolvedMap.getBooleanProp(dvt.AdfDiagramParser._ATTR_SELECTABLE, true));

  node.setDraggable(node.isSelectable() ? resolvedMap.getBooleanProp(dvt.AdfDiagramParser._ATTR_AF_DRAGGABLE, true) : false);
  node.setDragFlavor(resolvedMap.getProperty(dvt.AdfDiagramParser._ATTR_AF_DRAG_FLAVOR));
  node.setDropFlavors(resolvedMap.getProperty(dvt.AdfDiagramParser._ATTR_AF_DROP_FLAVORS));

  var containerStyle = resolvedMap.getProperty(dvt.AdfDiagramParser._ATTR_CONTAINER_STYLE);
  if (containerStyle) {
    node.setContainerStyle(new dvt.CSSStyle(containerStyle));
  }
  var overviewStyle = resolvedMap.getProperty(dvt.AdfDiagramParser._ATTR_OVERVIEW_STYLE);
  if (overviewStyle) {
    node.setOverviewStyle(new dvt.CSSStyle(overviewStyle));
  }
  var overviewContainerStyle = resolvedMap.getProperty(dvt.AdfDiagramParser._ATTR_OVERVIEW_CONTAINER_STYLE);
  if (overviewContainerStyle) {
    node.setOverviewContainerStyle(new dvt.CSSStyle(overviewContainerStyle));
  }
};


/**
 * Gets an empty default template with the given dimensions
 *
 * @param {number} width the width of the template to create
 * @param {number} height the height of the template to create
 * @return {dvt.AfComponent} the empty default template
 */
dvt.AdfDiagramNodeDef.prototype.getDefaultNodeTemplate = function(width, height) {
  if (width && !height) {
    height = width;
  }
  if (height && !width) {
    width = height;
  }
  if (!width && !height) {
    width = 233; // HV default
    height = 330; // HV default
  }

  var spacer = new dvt.AfSpacer();
  spacer.setWidth(width + '');
  spacer.setHeight(height + '');
  return spacer;
};

dvt.AdfDiagramNodeDef.prototype.addShowPopupBehavior = function(spb) {
  if (!this._showPopupBehaviors) {
    this._showPopupBehaviors = [];
  }
  this._showPopupBehaviors.push(spb);
};

dvt.AdfDiagramNodeDef.prototype.getShowPopupBehaviors = function() {
  return this._showPopupBehaviors;
};

dvt.AdfDiagramNodeDef.prototype.addClientBehavior = function(bhvr) {
  if (!this._clientBehaviors) {
    this._clientBehaviors = [];
  }
  this._clientBehaviors.push(bhvr);
};

dvt.AdfDiagramNodeDef.prototype.getClientBehaviors = function() {
  return this._clientBehaviors;
};

dvt.AdfDiagramNodeDef.prototype.getContainerStyle = function() {
  return this._getPropMapProperty(dvt.AdfDiagramParser._ATTR_CONTAINER_STYLE);
};

dvt.AdfDiagramNodeDef.prototype.setContainerStyle = function(containerStyle) {
  this._setPropMapProperty(dvt.AdfDiagramParser._ATTR_CONTAINER_STYLE, containerStyle);
};

/**
 * @return {dvt.CSSStyle} the style of node in overview window when not is not disclosed
 */
dvt.AdfDiagramNodeDef.prototype.getOverviewStyle = function() {
  return this._getPropMapProperty(dvt.AdfDiagramParser._ATTR_OVERVIEW_STYLE);
};

/**
 * @param {dvt.CSSStyle} overviewStyle the style of node in overview window when not is not disclosed
 */
dvt.AdfDiagramNodeDef.prototype.setOverviewStyle = function(overviewStyle) {
  this._setPropMapProperty(dvt.AdfDiagramParser._ATTR_OVERVIEW_STYLE, overviewStyle);
};

/**
 * @return {dvt.CSSStyle} the style of node in overview window when not is disclosed
 */
dvt.AdfDiagramNodeDef.prototype.getOverviewContainerStyle = function() {
  return this._getPropMapProperty(dvt.AdfDiagramParser._ATTR_OVERVIEW_CONTAINER_STYLE);
};

/**
 * @param {dvt.CSSStyle} overviewContainerStyle the style of node in overview window when not is disclosed
 */
dvt.AdfDiagramNodeDef.prototype.setOverviewContainerStyle = function(overviewContainerStyle) {
  this._setPropMapProperty(dvt.AdfDiagramParser._ATTR_OVERVIEW_CONTAINER_STYLE, overviewContainerStyle);
};

// Prepend the prefix so that if the prepended properties have already been specified in the inlineStyle, the existing
// values will "win"
dvt.AdfDiagramNodeDef.prototype._prependInlineStyle = function(prefix, inlineStyle) {
  return prefix + (inlineStyle ? (';' + inlineStyle) : '');
};

dvt.AdfDiagramNodeDef.prototype._setPropMapProperty = function(name, value) {
  if (!this._propMap) {
    this._propMap = new dvt.PropMap();
  }
  this._propMap.setProperty(name, value);
};

dvt.AdfDiagramNodeDef.prototype._getPropMapProperty = function(name) {
  if (this._propMap) {
    return this._propMap.getProperty(name);
  }
  return null;
};
/**
 * @constructor
 */
var DvtAdfDiagramNodeExpandHandler = function(diagram, nodeId)
{
  this.Init(diagram, nodeId);
};

dvt.Obj.createSubclass(DvtAdfDiagramNodeExpandHandler, dvt.Obj, 'DvtAdfDiagramNodeExpandHandler');

DvtAdfDiagramNodeExpandHandler.prototype.Init = function(diagram, nodeId) {
  this._diagram = diagram;
  this._nodeId = nodeId;
};

/*DvtAdfDiagramNodeExpandHandler.prototype.HandleClick = function(event) {
  var node = this._diagram.getNodeById(this._nodeId);
  if (node._bStack) {
    this._diagram.isolateStack(this._nodeId, 1, dvt.AdfDiagram.ISOLATE_TYPE_UNION);
  }
  else {
    this._diagram.isolateNode(this._nodeId, 1, dvt.AdfDiagram.ISOLATE_TYPE_UNION);
  }
}*/

DvtAdfDiagramNodeExpandHandler.prototype.getTooltip = function() {
  return this._diagram.getResourcesMap()['additionalLinksTip'];
};
// Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
var DvtAdfDiagramOverviewUtils = {};

dvt.Obj.createSubclass(DvtAdfDiagramOverviewUtils, dvt.Obj, 'DvtAdfDiagramOverviewUtils');


/**
 * @protected
 * Creates the overview window content based on the current diagram state
 *
 * @param {dvt.AdfDiagram} diagram the parent diagram component
 * @param {dvt.Rectangle} fitBounds the bounds of the Diagram content
 * @param {object} styleMap a map containing the default style information for the overview window
 *
 * @return {dvt.Displayable} the overview window content
 */
DvtAdfDiagramOverviewUtils.CreateOverviewContent = function(diagram, fitBounds, styleMap) 
{
  var ovContent = new dvt.Container(diagram.getCtx(), 'dg_ovContent');
  diagram._overviewContent = ovContent;
  ovContent.setMouseEnabled(false);
  if (!diagram._overviewNodes)
  {
    diagram._overviewNodes = {};
  }
  //Bug 16493799: can't loop over map because order may be different in Flash, so
  //loop over array instead
  for (var nodeIndex = 0; nodeIndex < diagram._arNodeIds.length; nodeIndex++)
  {
    var nodeId = diagram._arNodeIds[nodeIndex];
    var node = diagram.getNodeById(nodeId);
    var ovNode = DvtAdfDiagramOverviewUtils.CreateOverviewNode(diagram, node, styleMap);
    diagram._overviewNodes[nodeId] = ovNode;
    DvtAdfDiagramOverviewUtils.PositionOverviewNode(diagram, node);

    var groupId = node.getData().getGroupId();
    if (groupId) {
      var groupNode = diagram._nodes[groupId];
      var ovGroupNode = DvtAdfDiagramOverviewUtils.GetOverviewNode(diagram, groupNode);
      if (ovGroupNode) {
        //add a special container for child nodes so that it can be
        //offset to reflect container header height and container padding
        if (!ovGroupNode._ovChildNodePane) {
          ovGroupNode._ovChildNodePane = new dvt.Container(diagram.getCtx(), ovGroupNode.getId() + '_cnp');
          ovGroupNode.addChild(ovGroupNode._ovChildNodePane);
          DvtAdfDiagramOverviewUtils._translateChildNodePane(diagram, groupNode);
        }
        ovGroupNode._ovChildNodePane.addChild(ovNode);
      }
    }
    else {
      ovContent.addChild(ovNode);
    }

    // Make sure previously set visibility is honored
    DvtAdfDiagramOverviewUtils.SetNodeVisible(diagram, node, node.getVisible());
  }
  DvtAdfDiagramOverviewUtils.ZoomToFitOverviewContent(diagram, fitBounds);

  var viewport = this.CalcAdjustedViewport(diagram, null);
  diagram._overviewWindow.setViewportDimensions(viewport);

  return ovContent;
};


/**
 * Performs a zoom-to-fit of the currently displayed overview window content
 *
 * @param {dvt.AdfDiagram} diagram the parent diagram component
 * @param {dvt.Rectangle} fitBounds the bounds of the Diagram content
 * @param {dvt.Animator} animator the animator for animating the zoom-to-fit
 */
DvtAdfDiagramOverviewUtils.ZoomToFitOverviewContent = function(diagram, fitBounds, animator) 
{
  var ovContent = diagram._overviewContent;
  var cw = diagram._overviewWindow.getContentAreaWidth();
  var ch = diagram._overviewWindow.getContentAreaHeight();
  //Bug 13830256: need to get zoom-to-fit bounds from diagram,
  //taking into account properties in the animator that haven't been
  //applied yet
  var dims = fitBounds ? fitBounds : diagram.GetViewBounds(animator);
  var dz = DvtAdfDiagramOverviewUtils.CalcOverviewScale(diagram, dims);
  if (!diagram.supportsVectorEffects()) {
    for (var nodeIndex = 0; nodeIndex < diagram._arNodeIds.length; nodeIndex++)
    {
      var nodeId = diagram._arNodeIds[nodeIndex];
      var node = diagram._overviewNodes[nodeId];
      var stroke = node.getStroke();//the original is immutable
      if (stroke) {
        stroke = stroke.clone();
        stroke.setWidth(stroke.getWidth() * ovContent.getScaleX() / dz);
        if (animator)
        {
          animator.addProp(dvt.Animator.TYPE_STROKE, node, node.getStroke, node.setStroke, stroke);
        }
        else
        {
          node.setStroke(stroke);
        }
      }
    }
  }
  if (animator)
  {
    animator.addProp(dvt.Animator.TYPE_NUMBER, ovContent, ovContent.getScaleX, ovContent.setScaleX, dz);
    animator.addProp(dvt.Animator.TYPE_NUMBER, ovContent, ovContent.getScaleY, ovContent.setScaleY, dz);
  }
  else
  {
    ovContent.setScale(dz, dz);
  }

  var tx = (cw - dims.w * dz) / 2 - dims.x * dz;
  var ty = (ch - dims.h * dz) / 2 - dims.y * dz;
  if (animator)
  {
    animator.addProp(dvt.Animator.TYPE_NUMBER, ovContent, ovContent.getTranslateX, ovContent.setTranslateX, tx);
    animator.addProp(dvt.Animator.TYPE_NUMBER, ovContent, ovContent.getTranslateY, ovContent.setTranslateY, ty);
  }
  else
  {
    ovContent.setTranslate(tx, ty);
  }
};


/**
 * Calculates the scale factor for the overview window content
 *
 * @param {dvt.AdfDiagram} diagram the parent diagram component
 * @param {dvt.Rectangle} ztfBounds the dimensions of the bounding box of the diagram content
 *
 * @return {number} the scale factor for the overview window content
 */
DvtAdfDiagramOverviewUtils.CalcOverviewScale = function(diagram, ztfBounds) 
{
  var cw = diagram._overviewWindow.getContentAreaWidth() - 20;
  var ch = diagram._overviewWindow.getContentAreaHeight() - 20;
  var dzx = cw / ztfBounds.w;
  var dzy = ch / ztfBounds.h;
  var dz = Math.min(dzx, dzy);
  return dz;
};

DvtAdfDiagramOverviewUtils.CreateOverviewNodeId = function(nodeId) 
{
  return 'ov_' + nodeId;
};

DvtAdfDiagramOverviewUtils._getNodeIdFromOverviewNode = function(ovNode) {
  return DvtAdfDiagramOverviewUtils._getNodeIdFromOverviewNodeId(ovNode.getId());
};

DvtAdfDiagramOverviewUtils._getNodeIdFromOverviewNodeId = function(ovNodeId) {
  return ovNodeId.substring(3);
};

DvtAdfDiagramOverviewUtils.CreateOverviewNode = function(diagram, node, styleMap) 
{
  var dims = node.getLayoutBounds();
  var ovNode = new dvt.Rect(diagram.getCtx(), 0, 0, dims.w, dims.h,
      DvtAdfDiagramOverviewUtils.CreateOverviewNodeId(node.getData().getId()));
  ovNode._disclosed = node.isDisclosed();
  ovNode._hiddenAncestor = node.isHiddenAncestor();
  var fill = DvtAdfDiagramOverviewUtils.CreateFill(node, styleMap);
  ovNode.setFill(fill);
  var stroke = DvtAdfDiagramOverviewUtils.CreateStroke(node, styleMap);
  if (stroke) {
    ovNode.setStroke(stroke);
  }
  DvtAdfDiagramOverviewUtils.setBorderRadius(ovNode, node, styleMap);
  return ovNode;
};


/**
 * Updates the rendering of an overview window node based upon the current state of the corresponding Diagram node
 *
 * @param {dvt.AdfDiagram} diagram the parent diagram component
 * @param {dvt.AdfDiagramNode} node the Diagram node corresponding to the overview window node to be updated
 * @param {dvt.Animator} animator an animator for animating the changes
 * @param {object} styleMap a map containing default style information for the overview window
 */
DvtAdfDiagramOverviewUtils.UpdateOverviewNode = function(diagram, node, animator, styleMap) 
{
  var ovNode = DvtAdfDiagramOverviewUtils.GetOverviewNode(diagram, node);
  if (ovNode) {
    if (ovNode._disclosed != node.isDisclosed() ||
        ovNode._hiddenAncestor != node.isHiddenAncestor()) {
      var fill = DvtAdfDiagramOverviewUtils.CreateFill(node, styleMap);
      var newStroke = DvtAdfDiagramOverviewUtils.CreateStroke(node, styleMap);
      if (animator) {
        if (!fill.equals(ovNode.getFill()))
          animator.addProp(dvt.Animator.TYPE_FILL, ovNode, ovNode.getFill, ovNode.setFill, fill);
        var oldStroke = ovNode.getStroke();
        if (oldStroke || newStroke) {//skip if both are null
          if (!oldStroke) {
            oldStroke = new dvt.SolidStroke('rgba(0,0,0,0)');
          }
          else if (!newStroke) {
            newStroke = new dvt.SolidStroke('rgba(0,0,0,0)');
          }
          //only need to add if strokes are different
          if (oldStroke.getColor() != newStroke.getColor() || oldStroke.getAlpha() != newStroke.getAlpha()) {
            ovNode.setStroke(oldStroke);
            animator.addProp(dvt.Animator.TYPE_STROKE, ovNode,
                ovNode.getStroke, ovNode.setStroke, newStroke);
          }
        }
      }
      else {
        ovNode.setFill(fill);
        ovNode.setStroke(newStroke);
      }
      ovNode._disclosed = node.isDisclosed();
      ovNode._hiddenAncestor = node.isHiddenAncestor();
    }

    var dims = node.getLayoutBounds();
    var oldWidth = ovNode.getWidth();
    var oldHeight = ovNode.getHeight();
    if (oldWidth != dims.w) {
      if (animator) {
        animator.addProp(dvt.Animator.TYPE_NUMBER, ovNode,
                         ovNode.getWidth, ovNode.setWidth, dims.w);
      }
      else {
        ovNode.setWidth(dims.w);
      }
    }
    if (oldHeight != dims.h) {
      if (animator) {
        animator.addProp(dvt.Animator.TYPE_NUMBER, ovNode,
                         ovNode.getHeight, ovNode.setHeight, dims.h);
      }
      else {
        ovNode.setHeight(dims.h);
      }
    }

    DvtAdfDiagramOverviewUtils._translateChildNodePane(diagram, node, animator);
  }
};


/**
 * Translates the child node pane of a disclosed overview window node based on the offset of the corresponding
 * Diagram node's child node pane
 *
 * @param {dvt.AdfDiagram} diagram the parent diagram component
 * @param {dvt.AdfDiagramNode} node the Diagram node corresponding to the overview window node to be updated
 * @param {dvt.Animator} animator the animator for animating the changes
 */
DvtAdfDiagramOverviewUtils._translateChildNodePane = function(diagram, node, animator) {
  var ovNode = DvtAdfDiagramOverviewUtils.GetOverviewNode(diagram, node);
  if (ovNode) {
    if (node.isDisclosed()) {
      var cth = node.getContainerTopHeight();
      var clw = node.getContainerLeftWidth();
      var cp = node.getContainerPadding();

      var childrenOffsetX = clw;
      var childrenOffsetY = cth;
      if (cp) {
        childrenOffsetX += cp.left;
        childrenOffsetY += cp.top;
      }

      if (ovNode._ovChildNodePane) {
        if (childrenOffsetX != ovNode._ovChildNodePane.getTranslateX()) {
          if (animator) {
            animator.addProp(dvt.Animator.TYPE_NUMBER, ovNode._ovChildNodePane,
                             ovNode._ovChildNodePane.getTranslateX,
                             ovNode._ovChildNodePane.setTranslateX,
                             childrenOffsetX);
          }
          else {
            ovNode._ovChildNodePane.setTranslateX(childrenOffsetX);
          }
        }
        if (childrenOffsetY != ovNode._ovChildNodePane.getTranslateY()) {
          if (animator) {
            animator.addProp(dvt.Animator.TYPE_NUMBER, ovNode._ovChildNodePane,
                             ovNode._ovChildNodePane.getTranslateY,
                             ovNode._ovChildNodePane.setTranslateY,
                             childrenOffsetY);
          }
          else {
            ovNode._ovChildNodePane.setTranslateY(childrenOffsetY);
          }
        }
      }
    }
  }
};
/**
 * Creates Fill by taking in custom values and filling the rest with default values
 *
 * @param {dvt.AdfDiagramNode} node the diagram node corresponding to the overview window node to be updated
 * @param {Object} styleMap the default overview styles
 * @return {dvt.SolidFill} fill for the overview window
 */
DvtAdfDiagramOverviewUtils.CreateFill = function(node, styleMap) 
{
  var overviewStyle;
  if (!node.isDisclosed()) {
    overviewStyle = styleMap[dvt.AdfDiagram.OVERVIEW_STYLE];
    if (node.getOverviewStyle()) {
      //Merge styles with individual node styles taking priority
      overviewStyle = node.getOverviewStyle().clone().mergeUnder(overviewStyle);
    }
  }
  else {
    overviewStyle = styleMap[dvt.AdfDiagram.OVERVIEW_CONTAINER_STYLE];
    if (node.getOverviewContainerStyle()) {
      //Merge styles with individual node styles taking priority
      overviewStyle = node.getOverviewContainerStyle().clone().mergeUnder(overviewStyle);
    }
  }
  var nodeColor = overviewStyle.getStyle(dvt.CSSStyle.BACKGROUND_COLOR);
  return new dvt.SolidFill(dvt.ColorUtils.makeRGB(dvt.ColorUtils.getRed(nodeColor), dvt.ColorUtils.getGreen(nodeColor), dvt.ColorUtils.getBlue(nodeColor))
                          , node.isHiddenAncestor() ? 0 : Math.min(dvt.ColorUtils.getAlpha(nodeColor), node.GetMaxAlpha()));
};

/**
 * Creates stroke by taking in custom values and filling the rest with default values
 * @param {dvt.AdfDiagramNode} node the diagram node corresponding to the overview window node to be updated
 * @param {Object} styleMap the default overview styles
 * @return {dvt.SolidStroke} stroke for the overview window
 */
DvtAdfDiagramOverviewUtils.CreateStroke = function(node, styleMap) 
{
  var overviewStyle;
  var customStyle;
  if (!node.isDisclosed()) {
    overviewStyle = DvtAdfDiagramOverviewUtils.getBorderStyles(styleMap[dvt.AdfDiagram.OVERVIEW_STYLE]);
    customStyle = DvtAdfDiagramOverviewUtils.getBorderStyles(node.getOverviewStyle());
    if (customStyle) {
      //Merge styles with individual node styles taking priority
      overviewStyle = customStyle.clone().mergeUnder(overviewStyle);
    }
  }
  else {
    overviewStyle = DvtAdfDiagramOverviewUtils.getBorderStyles(styleMap[dvt.AdfDiagram.OVERVIEW_CONTAINER_STYLE]);
    customStyle = DvtAdfDiagramOverviewUtils.getBorderStyles(node.getOverviewContainerStyle());
    if (customStyle) {
      //Merge styles with individual node styles taking priority
      overviewStyle = customStyle.clone().mergeUnder(overviewStyle);
    }
  }
  if (overviewStyle) {
    var borderColor = overviewStyle.getStyle(dvt.CSSStyle.BORDER_COLOR);
    var stroke = new dvt.SolidStroke(dvt.ColorUtils.makeRGB(dvt.ColorUtils.getRed(borderColor), dvt.ColorUtils.getGreen(borderColor), dvt.ColorUtils.getBlue(borderColor))
                                  , node.isHiddenAncestor() ? 0 : dvt.ColorUtils.getAlpha(borderColor), overviewStyle.getBorderWidth());
    if (overviewStyle.getStyle(dvt.CSSStyle.BORDER_STYLE)) {
      stroke.setType(dvt.Stroke.convertTypeString(overviewStyle.getStyle(dvt.CSSStyle.BORDER_STYLE)));
    }
    stroke.setFixedWidth(true);
    return stroke;
  }
  return null;
};

/**
 * Takes in a dvt.CSSStyle and returns just the border related styles
 *
 * @param {dvt.CSSStyle} style the styles of the overview window
 * @return {dvt.CSSStyle} CSS style with only border attributes, null if no border attributes
 */
DvtAdfDiagramOverviewUtils.getBorderStyles = function(style) {
  if (!style) {
    return;
  }
  var borderStyle = new dvt.CSSStyle();
  var value;
  var stylePresent = false;
  value = style.getStyle(dvt.CSSStyle.BORDER_COLOR);
  if (value) {
    borderStyle.setStyle(dvt.CSSStyle.BORDER_COLOR, value);
    stylePresent = true;
  }
  value = style.getStyle(dvt.CSSStyle.BORDER_WIDTH);
  if (value) {
    borderStyle.setStyle(dvt.CSSStyle.BORDER_WIDTH, value);
    stylePresent = true;
  }
  value = style.getStyle(dvt.CSSStyle.BORDER_STYLE);
  if (value) {
    borderStyle.setStyle(dvt.CSSStyle.BORDER_STYLE, value);
    stylePresent = true;
  }
  if (stylePresent) {
    return borderStyle;
  }
  return null;
};
/**
 * Takes in a dvt.CSSStyle and returns just the border related styles
 *
 * @param {dvt.Rect} ovNode the rect that the border radius is set
 * @param {dvt.AdfDiagramNode} node the node whose radius will be set in the overview Window
 * @param {Object} styleMap the default overview Styles
 */
DvtAdfDiagramOverviewUtils.setBorderRadius = function(ovNode, node, styleMap) {
  var borderRadius;
  var overviewStyle;
  if (!node.isDisclosed()) {
    overviewStyle = node.getOverviewStyle();
    if (overviewStyle) {
      borderRadius = overviewStyle.getStyle(dvt.CSSStyle.BORDER_RADIUS)
                      ? overviewStyle.getStyle(dvt.CSSStyle.BORDER_RADIUS)
                      : styleMap[dvt.AdfDiagram.OVERVIEW_STYLE].getStyle(dvt.CSSStyle.BORDER_RADIUS);
    }
    else {
      borderRadius = styleMap[dvt.AdfDiagram.OVERVIEW_STYLE].getStyle(dvt.CSSStyle.BORDER_RADIUS);
    }
  }
  else {
    overviewStyle = node.getOverviewContainerStyle();
    if (overviewStyle) {
      borderRadius = overviewStyle.getStyle(dvt.CSSStyle.BORDER_RADIUS)
                      ? overviewStyle.getStyle(dvt.CSSStyle.BORDER_RADIUS)
                     : styleMap[dvt.AdfDiagram.OVERVIEW_CONTAINER_STYLE].getStyle(dvt.CSSStyle.BORDER_RADIUS);
    }
    else {
      borderRadius = styleMap[dvt.AdfDiagram.OVERVIEW_CONTAINER_STYLE].getStyle(dvt.CSSStyle.BORDER_RADIUS);
    }
  }
  if (borderRadius) {
    var radArr = dvt.StringUtils.trim(borderRadius).split(' ');
    var num;
    if (radArr.length > 0 && radArr[0]) {
      num = parseFloat(radArr[0]);
      ovNode.setRx(num < 0 ? 0 : num);
    }
    if (radArr.length > 1 && radArr[1]) {
      num = parseFloat(radArr[1]);
      ovNode.setRy(num < 0 ? 0 : num);
    }
  }
};

DvtAdfDiagramOverviewUtils.PositionOverviewNode = function(diagram, node, animator) 
{
  var tx;
  var ty;
  if (animator)
  {
    tx = animator.getDestVal(node, node.getTranslateX);
    ty = animator.getDestVal(node, node.getTranslateY);
  }
  else
  {
    tx = node.getTranslateX();
    ty = node.getTranslateY();
  }
  DvtAdfDiagramOverviewUtils.SetNodePosition(diagram, node, tx, ty, animator);
};


/**
 * Zoom to fit the overview content and syncs the viewport with the diagram
 *
 * @param {dvt.AdfDiagram} diagram the parent diagram component
 * @param {dvt.Animator} animator the animator for animating the changes
 */
DvtAdfDiagramOverviewUtils.SyncOverviewContent = function(diagram, animator) {
  if (!diagram._overviewWindow) {
    return;
  }
  DvtAdfDiagramOverviewUtils.ZoomToFitOverviewContent(diagram, null, animator);

  var newViewport = DvtAdfDiagramOverviewUtils.CalcAdjustedViewport(diagram, animator);

  diagram._overviewWindow.setViewportDimensions(newViewport, animator);
};


/**
 * Calculates the overview window viewport based on the component state
 *
 * @param {dvt.AdfDiagram} diagram the parent Diagram component
 * @param {dvt.Animator} animator an animator which may contain component state that has not yet been applied
 */
DvtAdfDiagramOverviewUtils.CalcAdjustedViewport = function(diagram, animator) {
  return DvtAdfDiagramOverviewUtils._calcViewportFromMatrix(diagram, diagram.getPanZoomCanvas().getContentPaneMatrix(animator));
};


/**
 * Calculates the overview window viewport from a pan zoom canvas matrix
 *
 * @param {dvt.AdfDiagram} diagram the parent Diagram component
 * @param {dvt.Matrix} matrix the pan zoom canvas matrix
 */
DvtAdfDiagramOverviewUtils._calcViewportFromMatrix = function(diagram, matrix) {
  var defViewport = new dvt.Rectangle(0, 0, diagram.getWidth(), diagram.getHeight());

  var dx = matrix.getTx();
  var dy = matrix.getTy();
  var dz = matrix.getA();

  var newX = defViewport.x - dx / dz;
  var newY = defViewport.y - dy / dz;
  var newWidth = defViewport.w / dz;
  var newHeight = defViewport.h / dz;

  var adjViewport = new dvt.Rectangle(newX, newY, newWidth, newHeight);
  return adjViewport;
};


DvtAdfDiagramOverviewUtils.SetNodePosition = function(diagram, node, xx, yy, animator) 
{
  var ovNode = DvtAdfDiagramOverviewUtils.GetOverviewNode(diagram, node);
  if (ovNode)
  {
    var p = new dvt.Point(xx, yy);
    //Bug 13382113: node is already in its group's coord system, so
    //we can use the given coords directly and don't need to map to stage
    //and back
    if (animator)
    {
      animator.addProp(dvt.Animator.TYPE_NUMBER, ovNode,
                       ovNode.getTranslateX, ovNode.setTranslateX, p.x);
      animator.addProp(dvt.Animator.TYPE_NUMBER, ovNode,
                       ovNode.getTranslateY, ovNode.setTranslateY, p.y);
    }
    else
    {
      ovNode.setTranslate(p.x, p.y);
    }
  }
};

DvtAdfDiagramOverviewUtils.GetMaxAlpha = function(diagram, node) {
  var maxAlpha = node.GetMaxAlpha();
  if (node.isDisclosed()) {
    var styleMap = diagram.getOverviewWindowContentStyle();
    var disclosedNodeAlpha = parseFloat(DvtAdfDiagramOverviewUtils.getSkinStyleAttr(styleMap,
        dvt.AdfDiagram.OVERVIEW_DISCLOSED_ALPHA));
    if (isNaN(disclosedNodeAlpha))
      disclosedNodeAlpha = dvt.AdfDiagram.OVERVIEW_DISCLOSED_DFLT_ALPHA;
    maxAlpha = Math.min(disclosedNodeAlpha, maxAlpha);
  }
  return maxAlpha;
};

DvtAdfDiagramOverviewUtils.SetNodeAlpha = function(diagram, node, alpha, animator) 
{

  var ovNode = DvtAdfDiagramOverviewUtils.GetOverviewNode(diagram, node);
  if (ovNode)
  {
    var originalFill = ovNode.getFill();
    if (!originalFill)
      return;

    var newAlpha = Math.min(alpha, DvtAdfDiagramOverviewUtils.GetMaxAlpha(diagram, node));
    if (newAlpha != originalFill.getAlpha()) {
      var newFill = originalFill.clone();
      newFill.setAlpha(newAlpha);
      if (animator)
        animator.addProp(dvt.Animator.TYPE_FILL, ovNode, ovNode.getFill, ovNode.setFill, newFill);
      else
        ovNode.setFill(newFill);
    }
  }
};

DvtAdfDiagramOverviewUtils.SetNodeVisible = function(diagram, node, bVisible) 
{
  var ovNode = DvtAdfDiagramOverviewUtils.GetOverviewNode(diagram, node);
  if (ovNode)
  {
    ovNode.setVisible(bVisible);
  }
};

DvtAdfDiagramOverviewUtils.GetOverviewNode = function(diagram, node) 
{
  if (diagram._overviewWindow && diagram._overviewNodes)
  {
    return diagram._overviewNodes[node.getData().getId()];
  }

  return null;
};


/**
 * Handles DvtPanEvents from the panZoomCanvas
 *
 * @param {dvt.AdfDiagram} diagram the parent diagram component
 * @param {dvt.PanEvent} event the dvt.PanEvent from the panZoomCanvas
 */
DvtAdfDiagramOverviewUtils.HandlePanEvent = function(diagram, event) {
  var mat = diagram.getPanZoomCanvas().getContentPaneMatrix(event.getAnimator()).clone();
  mat.translate(event.getNewX() - event.getOldX(), event.getNewY() - event.getOldY());
  diagram._overviewWindow.setViewportDimensions(DvtAdfDiagramOverviewUtils._calcViewportFromMatrix(diagram, mat), event.getAnimator());
};


/**
 * Handles DvtZoomEvents from the panZoomCanvas
 *
 * @param {dvt.AdfDiagram} diagram the parent diagram component
 * @param {dvt.ZoomEvent} event the dvt.ZoomEvent from the panZoomCanvas
 */
DvtAdfDiagramOverviewUtils.HandleZoomEvent = function(diagram, event) {
  var mat = diagram.getPanZoomCanvas().getContentPaneMatrix(event.getAnimator()).clone();
  mat.scale(event.getNewZoom() / event.getOldZoom(), event.getNewZoom() / event.getOldZoom(), event.getCenterPoint().x, event.getCenterPoint().y);
  mat.translate(event.getTx(), event.getTy());
  diagram._overviewWindow.setViewportDimensions(DvtAdfDiagramOverviewUtils._calcViewportFromMatrix(diagram, mat), event.getAnimator());
};


DvtAdfDiagramOverviewUtils.getSkinStyleAttr = function(styleMap, attr) {
  if (styleMap && styleMap[attr] != 'undefined')
    return styleMap[attr];
  else
    return null;
};

DvtAdfDiagramOverviewUtils._createCustomAnimPlayable = function(diagram) {
  return new dvt.CustomAnimation(diagram.getCtx(), null, diagram.getAnimationDuration());
};

DvtAdfDiagramOverviewUtils.AnimateUpdate = function(diagram, dataAnimHandler, oldViewportRect, newOverviewWindow, 
    oldContent, newContent, oldNodes, newNodes, oldArNodeIds, newArNodeIds) {
  DvtAdfDiagramOverviewUtils._animateUpdateContent(diagram, dataAnimHandler, oldViewportRect, newOverviewWindow, oldContent, newContent);

  // Copy the object lists, since we will modify them
  var tmpNewNodes = {};
  for (var nodeId in newNodes) {
    tmpNewNodes[nodeId] = newNodes[nodeId];
  }
  newNodes = tmpNewNodes;
  var tmpOldNodes = {};
  for (var nodeId in oldNodes) {
    tmpOldNodes[nodeId] = oldNodes[nodeId];
  }
  oldNodes = tmpOldNodes;

  var skipNodes = {};

  // Loop through the two lists and diff the changes.
  // Note: This implementation considers changes in order to be updates.
  if (oldNodes) {
    //Bug 16493799: can't loop over map because order may be different in Flash, so
    //loop over array instead
    for (var nodeIndex = 0; nodeIndex < oldArNodeIds.length; nodeIndex++) {
      var oldNodeId = oldArNodeIds[nodeIndex];

      //check whether we need to skip this node
      if (skipNodes[oldNodeId]) {
        delete oldNodes[oldNodeId];
        delete newNodes[oldNodeId];
        continue;
      }

      var oldNode = oldNodes[oldNodeId];
      if (!oldNode) { // oldItem must exist for update or delete
        continue;
      }

      // Loop through the new list looking for a match
      var newNode = newNodes[oldNodeId];
      if (newNode) { // newItem must exist for update
        // Match found, remove the item from the new list since it's handled
        DvtAdfDiagramOverviewUtils._animateUpdate(diagram, dataAnimHandler, oldNode, newNode, oldNodes, newNodes, skipNodes);
        delete newNodes[oldNodeId];
      }
      // If no match found, it was a delete.
      else {
        DvtAdfDiagramOverviewUtils._animateDelete(diagram, dataAnimHandler, oldNode);
      }
    }
  }

  // All remaining objects in newNodes are inserts
  //Bug 16493799: can't loop over map because order may be different in Flash, so
  //loop over array instead
  var tmpNewArNodeIds = [];
  for (var nodeIndex = 0; nodeIndex < newArNodeIds.length; nodeIndex++) {
    var nodeId = newArNodeIds[nodeIndex];
    if (newNodes[nodeId]) {
      tmpNewArNodeIds.push(nodeId);
    }
  }
  for (var nodeIndex = 0; nodeIndex < tmpNewArNodeIds.length; nodeIndex++) {
    var nodeId = tmpNewArNodeIds[nodeIndex];

    //check whether we need to skip this node
    if (skipNodes[nodeId]) {
      delete oldNodes[nodeId];
      delete newNodes[nodeId];
      continue;
    }

    if (newNodes[nodeId]) { // must be valid object for insert
      DvtAdfDiagramOverviewUtils._animateInsert(diagram, dataAnimHandler, newNodes[nodeId]);
    }
  }
};

DvtAdfDiagramOverviewUtils._animateUpdateContent = function(diagram, dataAnimHandler, oldViewportRect, newOverviewWindow, oldContent, newContent) {
  var playable = null;

  var oldMat = oldContent.getMatrix();
  var newMat = newContent.getMatrix();
  if (!oldMat.equals(newMat)) {
    if (!playable) {
      playable = DvtAdfDiagramOverviewUtils._createCustomAnimPlayable(diagram);
    }
    newContent.setMatrix(oldMat);
    playable.getAnimator().addProp(dvt.Animator.TYPE_MATRIX, newContent, newContent.getMatrix, newContent.setMatrix, newMat);
  }

  var newViewportRect = newOverviewWindow.GetViewportRectangle();
  if (oldViewportRect.x != newViewportRect.x ||
      oldViewportRect.y != newViewportRect.y ||
      oldViewportRect.w != newViewportRect.w ||
      oldViewportRect.h != newViewportRect.h) {
    if (!playable) {
      playable = DvtAdfDiagramOverviewUtils._createCustomAnimPlayable(diagram);
    }
    newOverviewWindow.SetViewportRectangle(oldViewportRect);
    playable.getAnimator().addProp(dvt.Animator.TYPE_RECTANGLE, newOverviewWindow, newOverviewWindow.GetViewportRectangle, newOverviewWindow.SetViewportRectangle, newViewportRect);
  }

  if (playable) {
    dataAnimHandler.add(playable, DvtAdfDiagramDataAnimationHandler.PHASE_UPDATE);
  }
};

DvtAdfDiagramOverviewUtils._animateUpdate = function(diagram, dataAnimHandler, oldNode, newNode, oldNodes, newNodes, skipNodes) {
  var playable = null;

  //if visibility is changing, treat the update like an insert or delete,
  //and don't have to consider any other changes to the node
  var oldVisible = oldNode.getVisible();
  var newVisible = newNode.getVisible();
  if (oldVisible != newVisible) {
    if (!playable) {
      playable = DvtAdfDiagramOverviewUtils._createCustomAnimPlayable(diagram);
    }
    //don't process the descendants separately because the visibility
    //change will handle them
    DvtAdfDiagramOverviewUtils._skipDescendants(DvtAdfDiagramOverviewUtils._getNodeIdFromOverviewNode(newNode), oldNodes, newNodes, 0, skipNodes);
    if (oldVisible) {
      //treat like a delete
      newNode.getParent().addChild(oldNode);
      playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, oldNode, oldNode.getAlpha, oldNode.setAlpha, 0);
      dvt.Playable.appendOnEnd(playable, function() {
        oldNode.getParent().removeChild(oldNode);
        oldNode.destroy();
      });
      dataAnimHandler.add(playable, DvtAdfDiagramDataAnimationHandler.PHASE_DELETE);
    }
    else if (newVisible) {
      //treat like an insert
      var prevAlpha = newNode.getAlpha();
      newNode.setAlpha(0);
      playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, newNode, newNode.getAlpha, newNode.setAlpha, prevAlpha);
      dataAnimHandler.add(playable, DvtAdfDiagramDataAnimationHandler.PHASE_INSERT);
    }

    //the node is either appearing or disappearing, so no need to
    //worry about other changes
    //playable = null;
    return;
  }

  var oldTx = oldNode.getTranslateX();
  var newTx = newNode.getTranslateX();
  if (oldTx != newTx) {
    if (!playable) {
      playable = DvtAdfDiagramOverviewUtils._createCustomAnimPlayable(diagram);
    }
    newNode.setTranslateX(oldTx);
    playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, newNode, newNode.getTranslateX, newNode.setTranslateX, newTx);
  }

  var oldTy = oldNode.getTranslateY();
  var newTy = newNode.getTranslateY();
  if (oldTy != newTy) {
    if (!playable) {
      playable = DvtAdfDiagramOverviewUtils._createCustomAnimPlayable(diagram);
    }
    newNode.setTranslateY(oldTy);
    playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, newNode, newNode.getTranslateY, newNode.setTranslateY, newTy);
  }

  var oldW = oldNode.getWidth();
  var newW = newNode.getWidth();
  if (oldW != newW) {
    if (!playable) {
      playable = DvtAdfDiagramOverviewUtils._createCustomAnimPlayable(diagram);
    }
    newNode.setWidth(oldW);
    playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, newNode, newNode.getWidth, newNode.setWidth, newW);
  }

  var oldH = oldNode.getHeight();
  var newH = newNode.getHeight();
  if (oldH != newH) {
    if (!playable) {
      playable = DvtAdfDiagramOverviewUtils._createCustomAnimPlayable(diagram);
    }
    newNode.setHeight(oldH);
    playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, newNode, newNode.getHeight, newNode.setHeight, newH);
  }

  var oldAlpha = oldNode.getAlpha();
  var newAlpha = newNode.getAlpha();
  if (oldAlpha != newAlpha) {
    if (!playable) {
      playable = DvtAdfDiagramOverviewUtils._createCustomAnimPlayable(diagram);
    }
    newNode.setAlpha(oldAlpha);
    playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, newNode, newNode.getAlpha, newNode.setAlpha, newAlpha);
  }

  var oldFill = oldNode.getFill();
  var newFill = newNode.getFill();
  if (oldFill && newFill && oldFill.getColor() != newFill.getColor()) {
    if (!playable) {
      playable = DvtAdfDiagramOverviewUtils._createCustomAnimPlayable(diagram);
    }
    newNode.setFill(oldFill);
    playable.getAnimator().addProp(dvt.Animator.TYPE_FILL, newNode, newNode.getFill, newNode.setFill, newFill);
  }

  var oldStroke = oldNode.getStroke();
  var newStroke = newNode.getStroke();
  if (oldStroke || newStroke) {//skip if both are null
    if (!oldStroke) {
      oldStroke = new dvt.SolidStroke('rgba(0,0,0,0)');
    }
    else if (!newStroke) {
      newStroke = new dvt.SolidStroke('rgba(0,0,0,0)');
    }
    if (oldStroke.getColor() != newStroke.getColor() || oldStroke.getAlpha() != newStroke.getAlpha()) {
      if (!playable) {
        playable = DvtAdfDiagramOverviewUtils._createCustomAnimPlayable(diagram);
      }
      newNode.setStroke(oldStroke);
      playable.getAnimator().addProp(dvt.Animator.TYPE_STROKE, newNode, newNode.getStroke, newNode.setStroke, newStroke);
    }
  }

  if (oldNode._ovChildNodePane && newNode._ovChildNodePane) {
    var oldCnpTx = oldNode._ovChildNodePane.getTranslateX();
    var newCnpTx = newNode._ovChildNodePane.getTranslateX();
    if (oldCnpTx != newCnpTx) {
      if (!playable) {
        playable = DvtAdfDiagramOverviewUtils._createCustomAnimPlayable(diagram);
      }
      newNode._ovChildNodePane.setTranslateX(oldCnpTx);
      playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, newNode._ovChildNodePane, newNode._ovChildNodePane.getTranslateX, newNode._ovChildNodePane.setTranslateX, newCnpTx);
    }

    var oldCnpTy = oldNode._ovChildNodePane.getTranslateY();
    var newCnpTy = newNode._ovChildNodePane.getTranslateY();
    if (oldCnpTy != newCnpTy) {
      if (!playable) {
        playable = DvtAdfDiagramOverviewUtils._createCustomAnimPlayable(diagram);
      }
      newNode._ovChildNodePane.setTranslateY(oldCnpTy);
      playable.getAnimator().addProp(dvt.Animator.TYPE_NUMBER, newNode._ovChildNodePane, newNode._ovChildNodePane.getTranslateY, newNode._ovChildNodePane.setTranslateY, newCnpTy);
    }
  }

  if (playable) {
    dataAnimHandler.add(playable, DvtAdfDiagramDataAnimationHandler.PHASE_UPDATE);
  }
};

DvtAdfDiagramOverviewUtils._animateInsert = function(diagram, dataAnimHandler, newNode) {
  //TO DO
};

DvtAdfDiagramOverviewUtils._animateDelete = function(diagram, dataAnimHandler, oldNode) {
  //TO DO
};

DvtAdfDiagramOverviewUtils._skipDescendants = function(ancestorNodeId, oldNodes, newNodes, skipLevels, skipNodes) {
  if (!skipLevels || skipLevels < 0) {
    skipLevels = 0;
  }
  var node = newNodes[ancestorNodeId];
  if (node._ovChildNodePane) {
    var numChildren = node._ovChildNodePane.getNumChildren();
    for (var i = 0; i < numChildren; i++) {
      var childId = node._ovChildNodePane.getChildAt(i).getId();
      var nodeId = DvtAdfDiagramOverviewUtils._getNodeIdFromOverviewNodeId(childId);
      if (skipLevels < 1) {
        skipNodes[nodeId] = true;
      }
      DvtAdfDiagramOverviewUtils._skipDescendants(nodeId, oldNodes, newNodes, skipLevels - 1, skipNodes);
    }
  }
};
// Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
/**
 * @constructor
 */
dvt.AdfDiagramParser = function(diagram)
{
  this.Init(diagram);
};

dvt.Obj.createSubclass(dvt.AdfDiagramParser, dvt.Obj, 'dvt.AdfDiagramParser');

dvt.AdfDiagramParser._ELEM_DIAGRAM_EVENT = 'dg_e';
dvt.AdfDiagramParser._ELEM_DATA = 'd';
dvt.AdfDiagramParser._ELEM_EL = 'el';
dvt.AdfDiagramParser._ELEM_F = 'f';
dvt.AdfDiagramParser._ELEM_LEGEND = 'legend';
dvt.AdfDiagramParser._ELEM_PALETTE = 'palette';
dvt.AdfDiagramParser._ELEM_LINK = 'l';
dvt.AdfDiagramParser._ELEM_LINK_DATA = 'ld';
dvt.AdfDiagramParser._ELEM_NODE = 'n';
dvt.AdfDiagramParser._ELEM_NODE_DATA = 'nd';
dvt.AdfDiagramParser._ELEM_OVERVIEWWINDOW = 'overviewWindow';
dvt.AdfDiagramParser._ELEM_POINT_LOCATION = 'pLoc';
dvt.AdfDiagramParser._ELEM_TEMPLATES = 't';
dvt.AdfDiagramParser._ELEM_MDEF = 'mdef';
dvt.AdfDiagramParser._ELEM_MARKERDEF = 'markerDef';
dvt.AdfDiagramParser._ELEM_FILLDEF = 'fillDef';
dvt.AdfDiagramParser._ELEM_GRADIENT = 'g';
dvt.AdfDiagramParser._ELEM_CONTEXT_MENU = 'menus';
dvt.AdfDiagramParser._ELEM_RESOURCES = 'resources';
dvt.AdfDiagramParser._ELEM_SELECTORS = 'st';
dvt.AdfDiagramParser._ELEM_AF_STYLES = 'afStyles';
dvt.AdfDiagramParser._ELEM_CT_STYLES = 'ctStyles';
dvt.AdfDiagramParser._ELEM_SHOW_POPUP_BEHAVIOR = 'spb';
dvt.AdfDiagramParser._ELEM_CLIENT_BEHAVIOR = 'bhvr';

dvt.AdfDiagramParser._ATTR_AF_INLINE_STYLE = 'inlineStyle';
dvt.AdfDiagramParser._ATTR_AF_STYLE_CLASS = 'styleClass';
dvt.AdfDiagramParser._ATTR_AF_NAME = 'name';
dvt.AdfDiagramParser._ATTR_AF_RENDERED = 'rendered';
dvt.AdfDiagramParser._ATTR_AF_READ_ONLY = 'readOnly';
dvt.AdfDiagramParser._ATTR_AF_HAS_DRAG_SOURCE = 'hasDragSource';
dvt.AdfDiagramParser._ATTR_AF_DRAGGABLE = 'draggable';
/**
 * Drag flavor for the diagram component or the diagram node
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_AF_DRAG_FLAVOR = 'dragFlavor';
/**
 * Drop flavors for the diagram component or the diagram node
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_AF_DROP_FLAVORS = 'dropFlavors';

dvt.AdfDiagramParser._ATTR_AF_SHORT_DESC = 'shortDesc';
dvt.AdfDiagramParser._ATTR_ATTR_GRPS = 'attrGrps';
dvt.AdfDiagramParser._ATTR_UNGROUPED = 'ungrouped';
dvt.AdfDiagramParser._ATTR_HIDDEN = 'hidden';
dvt.AdfDiagramParser._ATTR_HEIGHT = 'height';
dvt.AdfDiagramParser._ATTR_NODE_HIGHLIGHT_BEHAVIOR = 'nodeHL';
dvt.AdfDiagramParser._ATTR_LINK_HIGHLIGHT_BEHAVIOR = 'linkHL';
dvt.AdfDiagramParser._ATTR_HIGHLIGHTED_NODES = 'hlNodes';
dvt.AdfDiagramParser._ATTR_HIGHLIGHTED_LINKS = 'hlLinks';
dvt.AdfDiagramParser._ATTR_ID = 'id';
dvt.AdfDiagramParser._ATTR_INIT_ANIM = 'initAnim';
dvt.AdfDiagramParser._ATTR_DATA_CHANGE_ANIM = 'dataChangeAnim';
dvt.AdfDiagramParser._ATTR_ANIMATION_DURATION = 'animDuration';
dvt.AdfDiagramParser._ATTR_CHILD_ID = 'ci';
dvt.AdfDiagramParser._ATTR_CLIENT_LAYOUTS = 'clientLayouts';
dvt.AdfDiagramParser._ATTR_DROP_TARGET_BACKGROUND_COLOR = 'dropTargetBackgroundColor';
dvt.AdfDiagramParser._ATTR_DROP_TARGET_BORDER_COLOR = 'dropTargetBorderColor';
dvt.AdfDiagramParser._ATTR_DROP_TARGET_OPACITY = 'dropTargetOpacity';
dvt.AdfDiagramParser._ATTR_END_CONNECTOR_TYPE = 'endConnectorType';
dvt.AdfDiagramParser._ATTR_GROUP_ID = 'gi';
dvt.AdfDiagramParser._ATTR_LABEL = 'label';
dvt.AdfDiagramParser._ATTR_LAYOUT = 'layout';
dvt.AdfDiagramParser._ATTR_LAYOUT_ATTRS = 'layoutAttrs';

/**
 * @const
 */
dvt.AdfDiagramParser.ATTR_DATA_PPR = 'dataPPR';

/**
 * Temporary workaround junit tests for legend data
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_LEGEND_JSON_DATA = 'legendJsonData';
/**
 * Component summary attribute
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_SUMMARY = 'summary';
/**
 * Link color attribute
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_LINK_COLOR = 'linkColor';
/**
 * Link style attribute
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_LINK_STYLE = 'linkStyle';
/**
 * Link type attribute
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_LINK_TYPE = 'linkType';
/**
 * Link width attribute
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_LINK_WIDTH = 'linkWidth';
/**
 * Link length attribute
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_LINK_LENGTH = 'linkLength';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_PARENT_ID = 'pi';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_POINT_X = 'pointX';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_POINT_Y = 'pointY';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_ROLLOVER_BEHAVIOR = 'rolloverBehavior';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_SELECTION_MODE = 'selectionMode';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_SELECTABLE = 'selectable';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_SELECTED_NODES = 'selectedNodes';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_SELECTED_LINKS = 'selectedLinks';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_SHOW_DRILL = 'showDrill';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_SHOW_ISOLATE = 'showIsolate';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_SHOW_RESTORE = 'showRestore';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_SHOW_EXPAND_CHILDREN = 'showExpandChildren';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_SHOW_NODE_ACTIONS = 'showNodeActions';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_SHOW_STACK = 'showStack';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_DISCLOSED_NODES = 'disclosedNodes';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_START_CONNECTOR_TYPE = 'startConnectorType';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_TEMPLATE_ID = 'ti';
//dvt.AdfDiagramParser._ATTR_TYPE = "type";
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_WIDTH = 'width';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_ISOLATED_NODES = 'isolatedNodes';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_EMPTY_TEXT = 'emptyText';

/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_MAX_ZOOM = 'maxZoom';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_MIN_ZOOM = 'minZoom';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_CONTROL_PANEL_BEHAVIOR = 'controlPanelBehavior';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_PANNING = 'pan';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_PANDIRECTION = 'panDirection';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_ZOOMING = 'zoom';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_CENTER_NODE = 'center';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_ROOT_NODE = 'root';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_ROW_INDEX = 'idx';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_CONTEXT_MENU_ID = 'cm';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_STRUCTURE_ONLY = 'so';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_CONTAINER = 'container';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_CHILDREN_FETCHED = 'cf';

/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._COLON = ':';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._SEMICOLON = ';';
/**
 * XML attribute for the default background overview node color
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_OV_WIN_NODE = 'ovNode';
/**
 * XML attribute for the default background overview disclosed node color
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_OV_WIN_DISCLOSED_NODE = 'ovDisclosedNode';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_OV_WIN_VIEWPORT_BG_COLOR = 'ovViewportBgColor';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_OV_WIN_VIEWPORT_BORDER_COLOR = 'ovViewportBorderColor';

//Button Style
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_BUTTON_CONTAINER = 'buttonContainer';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_BUTTON_FILL_TYPE = 'FillType';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_BUTTON_BACKGROUND_COLOR = 'BackgroundColor';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_BUTTON_BACKGROUND = 'Background';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_BUTTON_BOX_SHADOW = 'BoxShadow';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_BUTTON_BORDER_COLOR = 'BorderColor';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_BUTTON_LABEL = 'buttonLabel';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_ENABLED_STATE = '';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_HOVER_STATE = 'Hover';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_ACTIVE_STATE = 'Active';

/**
 * XML attribute for the layout behavior
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_LAYOUT_BEHAVIOR = 'layoutBehavior';

/**
 * XML attribute for the color of the selected node state
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_NODE_SELECT_COLOR = dvt.AdfDiagramNode.SELECTED_COLOR;


/**
 * XML attribute for the inner color of the hover node state
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_NODE_HOVER_INNER_COLOR = dvt.AdfDiagramNode.HOVER_INNER_COLOR;


/**
 * XML attribute for the outer color of the hover node state
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_NODE_HOVER_OUTER_COLOR = dvt.AdfDiagramNode.HOVER_OUTER_COLOR;


/**
 * XML attribute for the node container style
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_NODE_CONTAINER_STYLE = dvt.AdfDiagramNode.CONTAINER_STYLE;

/**
 * XML attribute for the non-disclosed node overview style
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_NODE_OVERVIEW_STYLE = dvt.AdfDiagramNode.OVERVIEW_STYLE;

/**
 * XML attribute for the disclosed node overview style
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_NODE_OVERVIEW_CONTAINER_STYLE = dvt.AdfDiagramNode.OVERVIEW_CONTAINER_STYLE;


/**
 * XML attribute for the node label style
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_NODE_LABEL_STYLE = dvt.AdfDiagramNode.LABEL_STYLE;


/**
 * XML attribute for the color of the selected link state
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_LINK_SELECT_COLOR = dvt.AdfDiagramLink.SELECTED_COLOR;


/**
 * XML attribute for the inner color of the hover link state
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_LINK_HOVER_INNER_COLOR = dvt.AdfDiagramLink.HOVER_INNER_COLOR;


/**
 * XML attribute for the outer color of the hover link state
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_LINK_HOVER_OUTER_COLOR = dvt.AdfDiagramLink.HOVER_OUTER_COLOR;


/**
 * XML attribute for the color of promoted links
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_LINK_PROMOTED_COLOR = dvt.AdfDiagramLink.PROMOTED_COLOR;


/**
 * XML attribute for the linkStyle of promoted links
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_LINK_PROMOTED_STYLE = dvt.AdfDiagramLink.PROMOTED_STYLE;


/**
 * XML attribute for the width of promoted links
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_LINK_PROMOTED_WIDTH = dvt.AdfDiagramLink.PROMOTED_WIDTH;


/**
 * XML attribute for the link label style
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_LINK_LABEL_STYLE = dvt.AdfDiagramLink.LABEL_STYLE;


/**
 * Map for translating skin linkStyles to the appropriate dvt.AdfDiagramLinkDef constants
 * @const
 * @private
 */
dvt.AdfDiagramParser._LINK_STYLE_CONVERTER = {'solid': dvt.AdfDiagramLinkDef.LINK_STYLE_SOLID,
  'dashed': dvt.AdfDiagramLinkDef.LINK_STYLE_DASH,
  'dotted': dvt.AdfDiagramLinkDef.LINK_STYLE_DOT,
  'dashed-dotted': dvt.AdfDiagramLinkDef.LINK_STYLE_DASH_DOT};

dvt.AdfDiagramParser._ATTR_BREADCRUMBS_STYLE = 'breadcrumbsStyle';
dvt.AdfDiagramParser._ATTR_BREADCRUMBS_HOVER_STYLE = 'breadcrumbsHoverStyle';
dvt.AdfDiagramParser._ATTR_BREADCRUMBS_ACTIVE_STYLE = 'breadcrumbsActiveStyle';
dvt.AdfDiagramParser._ATTR_BREADCRUMBS_SELECTED_STYLE = 'breadcrumbsSelectedStyle';

dvt.AdfDiagramParser._ATTR_CONTAINER_STYLE = 'containerStyle';
/**
 * Overview Style for nodes
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_OVERVIEW_STYLE = 'overviewStyle';
/**
 * Overview Style for disclosed nodes
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_OVERVIEW_CONTAINER_STYLE = 'overviewContainerStyle';

dvt.AdfDiagramParser._ATTR_PAN_X = 'panX';
dvt.AdfDiagramParser._ATTR_PAN_Y = 'panY';
dvt.AdfDiagramParser._ATTR_ZOOM = 'zoomPct';

dvt.AdfDiagramParser._ATTR_EVENT_TYPE = 'type';

dvt.AdfDiagramParser._ATTR_STACK_PREVIEW_COLOR = 'stackPreviewColor';
dvt.AdfDiagramParser._ATTR_STACK_BADGE_STYLE = 'stackCounterStyle';
dvt.AdfDiagramParser._ATTR_UNDERLAY_STYLE = 'stackStyle';

dvt.AdfDiagramParser._ATTR_PREVIEW_NODE = 'previewRow';
dvt.AdfDiagramParser._ATTR_PREVIEW_PAN_X = 'previewPanX';
dvt.AdfDiagramParser._ATTR_PREVIEW_PAN_Y = 'previewPanY';
dvt.AdfDiagramParser._ATTR_PREVIEW_ZOOM = 'previewZoom';

dvt.AdfDiagramParser._ATTR_BUTTON_STYLE_ENA = 'buttonStyleEna';
dvt.AdfDiagramParser._ATTR_BUTTON_STYLE_OVR = 'buttonStyleOvr';
dvt.AdfDiagramParser._ATTR_BUTTON_STYLE_DWN = 'buttonStyleDwn';

dvt.AdfDiagramParser._ATTR_PALETTE_SECTION_STYLE = 'paletteSectionStyle';
dvt.AdfDiagramParser._ATTR_PALETTE_SECTION_HOVER_STYLE = 'paletteSectionHoverStyle';
dvt.AdfDiagramParser._ATTR_PALETTE_SECTION_ACTIVE_STYLE = 'paletteSectionActiveStyle';
dvt.AdfDiagramParser._ATTR_PALETTE_ITEM_STYLE = 'paletteItemStyle';

/*-------------------------------------------------------------------------*/
/*   PanelCard                                                             */
/*-------------------------------------------------------------------------*/
dvt.AdfDiagramParser._ELEM_CARD = 'c';
dvt.AdfDiagramParser._ELEM_CARDS = 'cd';

//Bug 14534257 - HTML5: panelCard of expanded child node didn't sync to parent
dvt.AdfDiagramParser._ATTR_NEW_NODE_SDI_MAP = 'newNodeSdis';

//defer panelCard loading
dvt.AdfDiagramParser._ATTR_NODE_SDI_MAP = 'nodeShowDetailItems';

/**
 * @private
 * Panel Card Navigation button skin
 */
dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_SKIN = 'navButtonSkin';

//panelCard selector
dvt.AdfDiagramParser._ATTR_CARD_TEXT = 't';
dvt.AdfDiagramParser._ATTR_CARD_ICON = 'i';
dvt.AdfDiagramParser._ATTR_CARD_ID = 'id';
dvt.AdfDiagramParser._ATTR_CARD_INDEXES = 'cardIndexes';

//panel card skinning
dvt.AdfDiagramParser._ATTR_CARD_FILL_TYPE = 'cardFillType';
dvt.AdfDiagramParser._ATTR_CARD_BORDER_TYPE = 'cardBorderType';
dvt.AdfDiagramParser._ATTR_CARD_BORDER_COLOR = 'cardBorderColor';
dvt.AdfDiagramParser._ATTR_CARD_BACKGROUND_COLOR = 'cardBackgroundColor';
dvt.AdfDiagramParser._ATTR_CARD_BORDER_RADIUS = 'cardBorderRadius';

dvt.AdfDiagramParser._ATTR_CARD_HEADER_TEXT = 'cardHeaderStyle';
dvt.AdfDiagramParser._ATTR_CARD_HEADER_TEXT75 = 'cardHeaderStyle75';
dvt.AdfDiagramParser._ATTR_CARD_HEADER_TEXT50 = 'cardHeaderStyle50';
dvt.AdfDiagramParser._ATTR_CARD_HEADER_TEXT25 = 'cardHeaderStyle25';

dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_FILL_TYPE = 'cardNavButtonFillType';
dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_ICON_COLOR = 'cardNavButtonIconColor';
dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_BORDER_COLOR = 'cardNavButtonBorderColor';
dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_BACKGROUND_COLOR = 'cardNavButtonBackgroundColor';
dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_HOVER_FILL_TYPE = 'cardNavButtonHoverFillType';
dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_HOVER_ICON_COLOR = 'cardNavButtonHoverIconColor';
dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_HOVER_BORDER_COLOR = 'cardNavButtonHoverBorderColor';
dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_HOVER_BACKGROUND_COLOR = 'cardNavButtonHoverBackgroundColor';
dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_ACTIVE_FILL_TYPE = 'cardNavButtonActiveFillType';
dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_ACTIVE_ICON_COLOR = 'cardNavButtonActiveIconColor';
dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_ACTIVE_BORDER_COLOR = 'cardNavButtonActiveBorderColor';
dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_ACTIVE_BACKGROUND_COLOR = 'cardNavButtonActiveBackgroundColor';
dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_DISABLED_FILL_TYPE = 'cardNavButtonDisabledFillType';
dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_DISABLED_ICON_COLOR = 'cardNavButtonDisabledIconColor';
dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_DISABLED_BORDER_COLOR = 'cardNavButtonDisabledBorderColor';
dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_DISABLED_BACKGROUND_COLOR = 'cardNavButtonDisabledBackgroundColor';

dvt.AdfDiagramParser._ATTR_CARD_TRAIN_BUTTON_VISIBILITY = 'cardTrainButtonVisibility';
dvt.AdfDiagramParser._ATTR_CARD_TRAIN_BUTTON_BORDER_COLOR = 'cardTrainButtonBorderColor';
dvt.AdfDiagramParser._ATTR_CARD_TRAIN_BUTTON_BACKGROUND_COLOR = 'cardTrainButtonBackgroundColor';
dvt.AdfDiagramParser._ATTR_CARD_TRAIN_BUTTON_ACTIVE_BORDER_COLOR = 'cardTrainButtonActiveBorderColor';
dvt.AdfDiagramParser._ATTR_CARD_TRAIN_BUTTON_ACTIVE_BACKGROUND_COLOR = 'cardTrainButtonActiveBackgroundColor';

dvt.AdfDiagramParser._ATTR_CARD_TRAIN_BUTTON_ICON = 'cardTrainIcon';
dvt.AdfDiagramParser._ATTR_CARD_TRAIN_BUTTON_HOVER_ICON = 'cardTrainHoverIcon';
dvt.AdfDiagramParser._ATTR_CARD_TRAIN_BUTTON_ACTIVE_ICON = 'cardTrainActiveIcon';

dvt.AdfDiagramParser._ATTR_CARD_PREV_ICON = 'cardPrevIcon';
dvt.AdfDiagramParser._ATTR_CARD_PREV_HOVER_ICON = 'cardPrevHoverIcon';
dvt.AdfDiagramParser._ATTR_CARD_PREV_ACTIVE_ICON = 'cardPrevActiveIcon';
dvt.AdfDiagramParser._ATTR_CARD_PREV_DISABLED_ICON = 'cardPrevDisabledIcon';
dvt.AdfDiagramParser._ATTR_CARD_NEXT_ICON = 'cardNextIcon';
dvt.AdfDiagramParser._ATTR_CARD_NEXT_HOVER_ICON = 'cardNextHoverIcon';
dvt.AdfDiagramParser._ATTR_CARD_NEXT_ACTIVE_ICON = 'cardNextActiveIcon';
dvt.AdfDiagramParser._ATTR_CARD_NEXT_DISABLED_ICON = 'cardNextDisabledIcon';

dvt.AdfDiagramParser._SEARCH_EVENT = 'hv_sea'; // AdfDvtSearchEvent.SEARCH_EVENT
dvt.AdfDiagramParser._SEARCH_EVENT_TYPE_NEXT = 'next'; // AdfDvtSearchEvent.SEARCH_EVENT_TYPE_NEXT
dvt.AdfDiagramParser._FETCH_EVENT = 'dvtDiagramFetchEvent'; // Adfdvt.AdfDiagramFetchEvent.FETCH_EVENT
dvt.AdfDiagramParser._PANELCARD_FETCH_EVENT = 'hv_pcfet'; // AdfDvtPanelCardFetchEvent.PANELCARD_FETCH_EVENT

/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_SCROLLBAR_BACKGROUND = 'scrollbarBg';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_SCROLLBAR_BORDER_COLOR = 'scrollbarBorderColor';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_SCROLLBAR_HANDLE_COLOR = 'scrollbarHandleColor';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_SCROLLBAR_HANDLE_ACTIVE_COLOR = 'scrollbarHandleActiveColor';
/**
 * @const
 * @private
 */
dvt.AdfDiagramParser._ATTR_SCROLLBAR_HANDLE_HOVER_COLOR = 'scrollbarHandleHoverColor';


dvt.AdfDiagramParser.prototype.Init = function(diagram)
{
  this._diagram = diagram;
};

/**
 * Load XML Initial
 * @param {object} rootXmlNode  XML object to load
 * @param {object} paramKeys  Parameter keys
 * @param {object} paramValues  Parameter Values
 */
dvt.AdfDiagramParser.prototype.loadXmlInitial = function(rootXmlNode, paramKeys, paramValues) {
  if (dvt.AdfDiagramParser._ELEM_DIAGRAM_EVENT == rootXmlNode.getName()) {
    var diagramXmlNode = rootXmlNode.getChildNodes()[0]; // Event must have exactly one child which is the diagram xml

    var event = rootXmlNode.getAttr(dvt.AdfDiagramParser._ATTR_EVENT_TYPE);
    this._diagram._eventType = event;
    try {
      if (dvt.AdfDiagramParser._SEARCH_EVENT == event) {
        var clientRowKey = rootXmlNode.getAttr('clientRowKey');
        var searchText = rootXmlNode.getAttr('searchText');
        var searchType = dvt.AdfDiagramParser._SEARCH_EVENT_TYPE_NEXT;
        this._diagram.LoadSearchResultsData(diagramXmlNode, searchText, searchType, clientRowKey);
      }
      else if (dvt.AdfDiagramParser._FETCH_EVENT == event) {
        this.LoadDiagramElement(diagramXmlNode);
        var nodeId = rootXmlNode.getAttr('clientRowKey');
        var subtype = rootXmlNode.getAttr('subtype');
        this._diagram.getNodeById(nodeId).getData().setChildrenFetched(true);
        this._diagram._resetTransitionState(); // ensure TransitionUtils starts from scratch because there are new nodes/links to account for
        if (dvt.AdfDiagramFetchEvent.DRILL === subtype) {
          this._diagram.drillNode(nodeId);
        }
        else { // DISCLOSE
          this._diagram.setNodeDisclosed(nodeId, true, false, true);
        }
      }
      else if (dvt.AdfDiagramParser._PANELCARD_FETCH_EVENT == event) {
        var clientRowKey = rootXmlNode.getAttr('clientRowKey');
        var nextCard = rootXmlNode.getAttr('nextc');
        var hoverWindow = rootXmlNode.getAttr('pchw');
        this.ParseAdditionalPanelCardData(diagramXmlNode);
        this._diagram.applyPanelCardData(clientRowKey, nextCard, hoverWindow);
      }
    }
    finally {
      this._diagram._eventType = null;
    }
    this._diagram._handledEventXml = true;
  }
  else {
    this.LoadDiagramElement(rootXmlNode);
  }
};


/**
 * Loads the Diagram Element
 * @param {dvt.XmlNode} rootXmlNode root node to load diagram from
 * @protected
 */
dvt.AdfDiagramParser.prototype.LoadDiagramElement = function(rootXmlNode) {
  if (this.ParseDiagramNodeAttrs(rootXmlNode))
  {
    //Note: must parse af selectors and styles before template
    var selectorAttrs = null;
    var styleAttrs = null;

    var afSelectors = rootXmlNode.getElementsByTagName(dvt.AdfDiagramParser._ELEM_SELECTORS);
    if (afSelectors && afSelectors[0]) {
      selectorAttrs = afSelectors[0].getAttributes();
    }

    var afStyles = rootXmlNode.getElementsByTagName(dvt.AdfDiagramParser._ELEM_AF_STYLES);
    if (afStyles && afStyles[0]) {
      styleAttrs = afStyles[0].getAttributes();
    }

    //save selectors and styles
    if (selectorAttrs && styleAttrs) {
      dvt.AfStyleUtils.setTagSkinKeyMap(selectorAttrs, styleAttrs);
    }

    //custom Styles if any
    var ctStyles = rootXmlNode.getElementsByTagName(dvt.AdfDiagramParser._ELEM_CT_STYLES);
    if (ctStyles && ctStyles[0]) {
      var ctStylesAttrs = ctStyles[0].getAttributes();
      //console.log(ctStylesAttrs);
    }

    this.setPanelCardStyles();

    var childNodes = rootXmlNode.getChildNodes();
    for (var i = 0; i < childNodes.length; i++)
    {
      var node = childNodes[i];
      this.ParseDiagramNodeChild(node);
    }

    // After all the nodes and links have been parsed, parse the isolation state
    this.ParseDiagramIsolation(rootXmlNode);
  }
};

/**
 * Parses diagram-level attributes from an xml node.
 * @param {dvt.XmlNode} xmlNode The xml node to parse
 * @protected
 */
dvt.AdfDiagramParser.prototype.ParseDiagramNodeAttrs = function(xmlNode)
{
  var s;

  //Bug 13704008: parse the inlineStyle on the component tag
  this._diagram.parseComponentAttrs(xmlNode);
  this.parseScrollbarStylesAttrs(xmlNode);
  this.parseOverviewContentAttrs(xmlNode);
  this.parseStackingStyles(xmlNode);
  this.parseStackPreview(xmlNode);
  this.parseDiagramButtonStyles(xmlNode);
  this.parseSelection(xmlNode);
  this.parseBreadcrumbsAttrs(xmlNode);
  this.parsePaletteAttrs(xmlNode);

  // node, link skinning
  this.ParseNodeStyles(xmlNode);
  this.ParseLinkStyles(xmlNode);
  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CLIENT_LAYOUTS);
  if (s) {
    var layoutMap = eval('(' + s + ')');//From the server produced Json Object @HtmlUpdateOK
    for (var key in layoutMap) {
      try {
        var func = eval(layoutMap[key]['strMethod']);//From the server produced Json Object @HtmlUpdateOK
        this._diagram.getLayoutCallback().addLayout(key, func);
      }
      catch (e) {
        if (e.name == 'ReferenceError') {
          this._diagram.Log(e.name + ':' + e.message, '2'); // SEVERE
        }
      }
      if (layoutMap[key]['layoutAttrs']) {
        this._diagram.setLayoutAttributes(key, layoutMap[key]['layoutAttrs']);
      }
    }
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_LAYOUT);
  if (s)
    this._diagram.setLayout(s);

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_INIT_ANIM);
  if (s) {
    if (s == 'auto') {
      s = dvt.BlackBoxAnimationHandler.ALPHA_FADE;
    }
    this._diagram.setInitAnim(s);
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_DATA_CHANGE_ANIM);
  if (s) {
    this._diagram.setDataChangeAnim(s);
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_ANIMATION_DURATION);
  if (s)
    this._diagram.setAnimationDuration(parseInt(s) / 1000);

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_NODE_HIGHLIGHT_BEHAVIOR);
  if (s)
    this._diagram.setNodeHighlightBehavior(s);

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_LINK_HIGHLIGHT_BEHAVIOR);
  if (s)
    this._diagram.setLinkHighlightBehavior(s);

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_HIGHLIGHTED_NODES);
  if (s)
    this._diagram._highlightedNodes = s.split(',');

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_HIGHLIGHTED_LINKS);
  if (s)
    this._diagram._highlightedLinks = s.split(',');

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_DISCLOSED_NODES);
  if (s)
    this._diagram._disclosedNodes = s.split(',');
  else
    this._diagram._disclosedNodes = null;

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_ATTR_GRPS);
  if (s)
  {
    //this._diagram.setAttributeGroups(s.split(dvt.AdfDiagramParser._SEMICOLON));
    this._diagram.setAttributeGroups(s.split(/[\s;]+/));
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_UNGROUPED);
  if (s)
  {
    this._diagram.setExplodedStacks(s.split(','));
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_HIDDEN);
  if (s)
  {
    this._diagram.setHiddenAttributeGroups(s.split(','));
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_ROLLOVER_BEHAVIOR);
  if (s) {
    this._diagram.setRolloverBehavior(s);
  }
  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_AF_READ_ONLY);
  if (s) {
    this._diagram.setReadOnly('true' == s);
  }
  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_AF_HAS_DRAG_SOURCE);
  if (s) {
    this._diagram.setHasDragSource('true' == s);
  }
  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_MAX_ZOOM);
  if (s) {
    var maxZoom = 2.0;
    var f = parseFloat(s);
    if (f > 0) {
      maxZoom = f;
    }
    this._diagram.setMaxZoom(maxZoom);
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_MIN_ZOOM);
  if (s) {
    var minZoom = 0.0;
    var f = parseFloat(s);
    if (f > 0) {
      minZoom = Math.min(f, maxZoom);
    }
    this._diagram.setMinZoom(minZoom);
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CONTROL_PANEL_BEHAVIOR);
  if (s) {
    this._diagram.setControlPanelBehavior(s);
  }

  var displayedControls = this._diagram.getDisplayedControls();

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_PANNING);
  var panningEnabled = 'none' != s;
  this._diagram.SetPanningEnabled(panningEnabled);
  if (!panningEnabled) {
    displayedControls = displayedControls & ~dvt.ControlPanel.CONTROLS_CENTER_BUTTON;
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_ZOOMING);
  var zoomingEnabled = 'none' != s;
  this._diagram.SetZoomingEnabled(zoomingEnabled);
  if (!zoomingEnabled) {
    displayedControls = displayedControls & ~dvt.ControlPanel.CONTROLS_ZOOM;
    displayedControls = displayedControls & ~dvt.ControlPanel.CONTROLS_ZOOM_TO_FIT_BUTTON;
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_SHOW_RESTORE);
  if (s) {
    var showRestore = (s == 'off' ? 'off' : 'on');
    this._diagram.SetShowRestore(showRestore);
  }

  this._diagram.setDisplayedControls(displayedControls);

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CENTER_NODE);
  if (s) {
    this._diagram.setCenterNode(s);
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_ROOT_NODE);
  if (s) {
    this._diagram._drillRoot = s; // processed in diagram.stopParsingData
  }

  var panZoom = {};
  var setPanZoom = false;
  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_PAN_X);
  if (s) {
    setPanZoom = true;
    panZoom.panX = parseFloat(s);
  }
  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_PAN_Y);
  if (s) {
    setPanZoom = true;
    panZoom.panY = parseFloat(s);
  }
  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_ZOOM);
  if (s) {
    setPanZoom = true;
    panZoom.zoom = parseFloat(s);
  }
  if (setPanZoom) {
    this._diagram._panZoom = panZoom;
  }

  //drop target styles
  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_DROP_TARGET_BACKGROUND_COLOR);
  if (s) {
    this._diagram.setDropFeedbackFillColor(s);
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_DROP_TARGET_BORDER_COLOR);
  if (s) {
    this._diagram.setDropFeedbackBorderColor(s);
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_DROP_TARGET_OPACITY);
  if (s) {
    this._diagram.setDropFeedbackFillAlpha(parseFloat(s));
  }

  // panelCard detailItemIndexes
  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CARD_INDEXES);
  if (s) {
    this._diagram.setCardIndexes(s);
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_EMPTY_TEXT);
  if (s) {
    this._diagram.setEmptyText(s);
  }

  // panelCard skinning keys
  this.ParsePanelCardAttrs(xmlNode);
  this.ParsePanelCardHeaderAttrs(xmlNode);
  this.ParsePanelCardNavButtonAttrs(xmlNode);
  this.ParsePanelCardTrainButtonAttrs(xmlNode);
  this.ParsePanelCardNextPrevButtonAttrs(xmlNode);

  return true;
};





dvt.AdfDiagramParser.prototype.ParseDiagramIsolation = function(xmlNode) {
  var s;

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_ISOLATED_NODES);
  if (s) {
    this._diagram._isolatedNodes = s.split(',');
    this._diagram._isolatedNodeMap = {};
    for (var i = 0; i < this._diagram._isolatedNodes.length; i++) {
      this._diagram._isolatedNodeMap[this._diagram._isolatedNodes[i]] = false;
    }
  }
};

dvt.AdfDiagramParser.prototype.parseSelection = function(xmlNode) {
  var s;

  //Parse Selection Mode
  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_SELECTION_MODE);
  this._diagram.setSelectionMode(s); // Set even when null to enable "sticky" link highlighting

  //Parse Selected Nodes
  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_SELECTED_NODES);
  if (s)
    this._diagram._selectedNodes = s.split(',');

  //Parse Selected Links
  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_SELECTED_LINKS);
  if (s)
    this._diagram._selectedLinks = s.split(',');
};

dvt.AdfDiagramParser.SplitLayoutAttrsMap = function(layoutAttrsString) {
  var arLayoutAttrs = layoutAttrsString.split(dvt.AdfDiagramParser._SEMICOLON);
  var mapLayoutAttrs = {};
  var numLayoutAttrs = arLayoutAttrs.length;
  var layoutAttr;
  for (var iLayoutAttr = 0; iLayoutAttr < numLayoutAttrs; iLayoutAttr++)
  {
    layoutAttr = arLayoutAttrs[iLayoutAttr];
    var arLAKeyVal = layoutAttr.split(dvt.AdfDiagramParser._COLON);
    if (arLAKeyVal.length > 1)
    {
      mapLayoutAttrs[arLAKeyVal[0]] = arLAKeyVal[1];
    }
  }
  return mapLayoutAttrs;
};


/**
 * Parse Diagram Node
 * @param {dvt.XmlNode} xmlNode  node to parse
 */
dvt.AdfDiagramParser.prototype.ParseDiagramNodeChild = function(xmlNode)
{
  var nodeName = xmlNode.getName();
  switch (nodeName)
  {
    case dvt.AdfDiagramParser._ELEM_TEMPLATES:
      this.ParseTemplates(xmlNode);
      break;
    case dvt.AdfDiagramParser._ELEM_DATA:
      this.ParseData(xmlNode);
      break;
    case dvt.AdfDiagramParser._ELEM_SHOW_POPUP_BEHAVIOR:
      this.ParseShowPopupBehavior(xmlNode);
      break;
    case dvt.AdfDiagramParser._ELEM_CONTEXT_MENU:
      // Make sure the handler is created
      this._diagram._contextMenuHandler = new dvt.ContextMenuHandler(this._diagram.getCtx(), xmlNode);
      this._diagram.getEventManager().setContextMenuHandler(this._diagram._contextMenuHandler);
      break;
    case dvt.AdfDiagramParser._ELEM_LEGEND:
      this.ParseLegend(xmlNode);
      break;
    case dvt.AdfDiagramParser._ELEM_OVERVIEWWINDOW:
      this.ParseOverviewWindow(xmlNode);
      break;
    case dvt.AdfDiagramParser._ELEM_RESOURCES:
      this.ParseResources(xmlNode);
      break;
    case dvt.AdfDiagramParser._ELEM_CARDS:
      this.ParseCards(xmlNode);
      break;
    case dvt.SearchPanel.ELEM_SEARCH_RESULTS_TEMPLATE:
    case dvt.SearchPanel.ELEM_SEARCH_ADVANCED_TEMPLATE:
    case dvt.SearchPanel.ELEM_SEARCH_RESULTS_DATA:
      this._diagram.getSearchPanel(true).parseNode(xmlNode);
      break;
  }
};

dvt.AdfDiagramParser.prototype.ParseTemplates = function(xmlNode)
{
  var childNodes = xmlNode.getChildNodes();
  for (var i = 0; i < childNodes.length; i++)
  {
    var node = childNodes[i];
    var nodeName = node.getName();
    switch (nodeName)
    {
      case dvt.AdfDiagramParser._ELEM_NODE:
        this.ParseNode(node);
        break;
      case dvt.AdfDiagramParser._ELEM_LINK:
        this.ParseLink(node);
        break;
    }
  }
};

dvt.AdfDiagramParser.prototype.CreateNodeDef = function() {
  return new dvt.AdfDiagramNodeDef();
};

dvt.AdfDiagramParser.prototype.ParseNode = function(xmlNode)
{
  var nodeDef = this.CreateNodeDef();
  this.ParseNodeAttrs(xmlNode, nodeDef);

  var childNodes = xmlNode.getChildNodes();
  if (childNodes && childNodes.length > 0)
  {
    var template;
    for (var i = 0; i < childNodes.length; i++)
    {
      var tmpNode = childNodes[i];
      if (tmpNode.getName() === dvt.AdfDiagramParser._ELEM_F)
      {
        var nameAttr = tmpNode.getAttr(dvt.AdfDiagramParser._ATTR_AF_NAME);
        if (nameAttr === 'zoom100' ||
            nameAttr === 'zoom75' ||
            nameAttr === 'zoom50' ||
            nameAttr === 'zoom25')
        {
          template = dvt.AfComponentFactory.parseXml(tmpNode);
          if (template)
          {
            nodeDef.setNodeTemplate(template, nameAttr);
          }
        }
        else if (nameAttr === 'label')
        {
          template = dvt.AfComponentFactory.parseXml(tmpNode);
          if (template)
          {
            nodeDef.setLabelTemplate(template);
          }
        }
        else if (nameAttr === 'overlay') {
          template = dvt.AfComponentFactory.parseXml(tmpNode);
          if (template) {
            nodeDef.setOverlayTemplate(template);
          }
        }
        else if (nameAttr === 'background') {
          template = dvt.AfComponentFactory.parseXml(tmpNode);
          if (template)
          {
            nodeDef.setBackgroundTemplate(template);
          }
        }
        else if (nameAttr === 'containerTop') {
          template = dvt.AfComponentFactory.parseXml(tmpNode);
          if (template)
          {
            nodeDef.setContainerTopTemplate(template);
          }
        }
        else if (nameAttr === 'containerBottom') {
          template = dvt.AfComponentFactory.parseXml(tmpNode);
          if (template)
          {
            nodeDef.setContainerBottomTemplate(template);
          }
        }
        else if (nameAttr === 'containerLeft') {
          template = dvt.AfComponentFactory.parseXml(tmpNode);
          if (template)
          {
            nodeDef.setContainerLeftTemplate(template);
          }
        }
        else if (nameAttr === 'containerRight') {
          template = dvt.AfComponentFactory.parseXml(tmpNode);
          if (template)
          {
            nodeDef.setContainerRightTemplate(template);
          }
        }
        else if (nameAttr === 'containerStart') {
          template = dvt.AfComponentFactory.parseXml(tmpNode);
          if (template)
          {
            // Let left/right templates "win" if they've been explicitly specified
            if (dvt.Agent.isRightToLeft()) {
              if (!nodeDef.getContainerRightTemplate()) {
                nodeDef.setContainerRightTemplate(template);
              }
            }
            else {
              if (!nodeDef.getContainerLeftTemplate()) {
                nodeDef.setContainerLeftTemplate(template);
              }
            }
          }
        }
        else if (nameAttr === 'containerEnd') {
          template = dvt.AfComponentFactory.parseXml(tmpNode);
          if (template)
          {
            // Let left/right templates "win" if they've been explicitly specified
            if (dvt.Agent.isRightToLeft()) {
              if (!nodeDef.getContainerLeftTemplate()) {
                nodeDef.setContainerLeftTemplate(template);
              }
            }
            else {
              if (!nodeDef.getContainerRightTemplate()) {
                nodeDef.setContainerRightTemplate(template);
              }
            }
          }
        }

        //when removing child node, need to decrement i to compensate
        childNodes.splice(i, 1);
        i--;
      }
      else if (tmpNode.getName() === dvt.AdfDiagramParser._ELEM_SHOW_POPUP_BEHAVIOR) {
        var spb = dvt.AfComponentFactory.parseElement(tmpNode);
        if (spb) {
          nodeDef.addShowPopupBehavior(spb);
        }
      }
      else if (tmpNode.getName() === dvt.AdfDiagramParser._ELEM_CLIENT_BEHAVIOR) {
        var bhvr = dvt.AfComponentFactory.parseElement(tmpNode);
        if (bhvr) {
          nodeDef.addClientBehavior(bhvr);
        }
      }
    }
  }

  this._diagram.addNodeDef(nodeDef);
};


/**
 * Parses properties for node templates
 * @param {dvt.XmlNode} xmlNode the xml node containing the the node template properties
 * @param {dvt.AdfDiagramNodeDef} nodeDef the node definition object on which to set properties
 */
dvt.AdfDiagramParser.prototype.ParseNodeAttrs = function(xmlNode, nodeDef)
{
  var s;

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_ID);
  nodeDef.setId(s);

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_WIDTH);
  if (s) {
    nodeDef.setWidth(s);
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_HEIGHT);
  if (s) {
    nodeDef.setHeight(s);
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_AF_INLINE_STYLE);
  nodeDef.setInlineStyle(s);

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_AF_STYLE_CLASS);
  nodeDef.setStyleClass(s);

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_AF_READ_ONLY);
  if (s) {
    nodeDef.setReadOnly(s);
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_AF_DRAGGABLE);
  if (s && s.toLowerCase() === 'no')
    nodeDef.setDraggable(false);
  else if (s && s.toLowerCase() === 'yes') {
    nodeDef.setDraggable(true);
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_SELECTABLE);
  if (s) {
    nodeDef.setSelectable(s);
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_AF_SHORT_DESC);
  if (s)
  {
    nodeDef.setShortDesc(s);
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_LAYOUT);
  if (s)
  {
    nodeDef.setLayout(s);
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_LABEL);
  if (s)
  {
    nodeDef.setLabel(s);
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CONTAINER_STYLE);
  if (s) {
    nodeDef.setContainerStyle(s);
  }
};

dvt.AdfDiagramParser.prototype.parseLinkTemplate = function(xmlNode) {
  var linkDef = new dvt.AdfDiagramLinkDef();
  var s;

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_ID);
  linkDef.setId(s);

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_LINK_WIDTH);
  if (s)
  {
    linkDef.setLinkWidth(s);
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_LINK_LENGTH);
  if (s)
  {
    linkDef.setLinkLength(s);
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_AF_READ_ONLY);
  if (s) {
    linkDef.setReadOnly(s);
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_SELECTABLE);
  if (s) {
    linkDef.setSelectable(s);
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_LINK_COLOR);
  if (s && (s = dvt.AdfDiagramParser.validateColor(s)))
  {
    linkDef.setLinkColor(s);
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_LINK_TYPE);
  if (s)
  {
    linkDef.setLinkType(s);
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_LABEL);
  if (s)
  {
    linkDef.setLabel(s);
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_LINK_STYLE);
  if (s)
  {
    linkDef.setLinkStyle(s);
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_AF_RENDERED);
  if (s)
  {
    linkDef.setRendered(s);
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_AF_SHORT_DESC);
  if (s)
  {
    linkDef.setShortDesc(s);
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_END_CONNECTOR_TYPE);
  if (s)
  {
    linkDef.setEndConnectorType(s);
  }
  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_START_CONNECTOR_TYPE);
  if (s)
  {
    linkDef.setStartConnectorType(s);
  }
  var childNodes = xmlNode.getChildNodes();
  if (childNodes && childNodes.length > 0)
  {
    var template;
    for (var i = 0; i < childNodes.length; i++)
    {
      var tmpNode = childNodes[i];
      if (tmpNode.getName() === dvt.AdfDiagramParser._ELEM_F)
      {
        var nameAttr = tmpNode.getAttr(dvt.AdfDiagramParser._ATTR_AF_NAME);
        if (nameAttr === 'endConnector' ||
            nameAttr === 'startConnector')
        {
          template = dvt.AfComponentFactory.parseXml(tmpNode);
          if (template)
          {
            linkDef.setConnectorTemplate(template, nameAttr);
          }
        }
        else if (nameAttr === 'label')
        {
          template = dvt.AfComponentFactory.parseXml(tmpNode);
          if (template)
          {
            linkDef.setLabelTemplate(template);
          }
        }
      }
      else if (tmpNode.getName() === dvt.AdfDiagramParser._ELEM_SHOW_POPUP_BEHAVIOR) {
        var spb = dvt.AfComponentFactory.parseElement(tmpNode);
        if (spb) {
          linkDef.addShowPopupBehavior(spb);
        }
      }
    }
  }
  return linkDef;
};

dvt.AdfDiagramParser.prototype.ParseLink = function(xmlNode)
{
  var linkDef = this.parseLinkTemplate(xmlNode);
  this._diagram.addLinkDef(linkDef);
};

dvt.AdfDiagramParser.prototype.ParseData = function(xmlNode)
{
  var childNodes = xmlNode.getChildNodes();
  this._diagram.startParsingData();
  for (var i = 0; i < childNodes.length; i++)
  {
    var node = childNodes[i];
    var nodeName = node.getName();
    switch (nodeName)
    {
      case dvt.AdfDiagramParser._ELEM_NODE_DATA:
        var nodeData = this.ParseNodeData(node);
        this._diagram.addNodeData(nodeData);
        break;
      case dvt.AdfDiagramParser._ELEM_LINK_DATA:
        var linkData = this.ParseLinkData(node);
        this._diagram.addLinkData(linkData);
        break;
    }
  }
  this._diagram.stopParsingData();
};


/**
 * Parse show popup behavior
 * @param {dvt.XmlNode} xmlNode  node to parse
 */
dvt.AdfDiagramParser.prototype.ParseShowPopupBehavior = function(xmlNode) {
  if (xmlNode) {
    var childNodes = xmlNode.getChildNodes();
    for (var j = 0; j < childNodes.length; j++) {
      var spb = dvt.AfComponentFactory.parseElement(childNodes[j]);
      if (spb) {
        this._diagram.addShowPopupBehavior(spb);
      }
    }
  }
};

dvt.AdfDiagramParser.prototype.CreateNodeData = function() {
  return new dvt.AdfDiagramNodeData();
};

dvt.AdfDiagramParser.prototype.ParseNodeData = function(xmlNode)
{
  var nodeData = this.CreateNodeData();
  var s;

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_ID);
  nodeData.setId(s);

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_GROUP_ID);
  nodeData.setGroupId(s);

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_STRUCTURE_ONLY);
  if ('true' === s) {
    nodeData.setStructureOnly(true);
  }
  else {
    s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_TEMPLATE_ID);
    nodeData.setTemplateId(s);

    s = parseInt(xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_ROW_INDEX));
    nodeData.setRowIndex(s);

    s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_ATTR_GRPS);
    if (s)
    {
      nodeData.setAttributeGroups(s.split(dvt.AdfDiagramParser._SEMICOLON));
    }

    //Bug 13745138: pay attention to showIsolate attribute
    s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_SHOW_ISOLATE);
    if (s) {
      nodeData.setShowIsolate('false' !== s);
    }

    s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_SHOW_EXPAND_CHILDREN);
    if (s) {
      nodeData.setShowExpandChildren('false' !== s);
    }

    s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_SHOW_DRILL);
    if (s) {
      nodeData.setShowDrill('false' !== s);
    }

    s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_SHOW_STACK);
    if (s) {
      nodeData.setShowStack('false' !== s);
    }

    s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_SHOW_NODE_ACTIONS);
    if (s) {
      nodeData.setShowNodeActions('false' !== s);
    }

    s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_LAYOUT_ATTRS);
    if (s) {
      var layoutAttrs = eval('(' + s + ')');//From the server produced Json Object @HtmlUpdateOK
      nodeData.setLayoutAttributes(layoutAttrs);
    }

    s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CONTAINER);
    if (s) {
      nodeData.setContainer('true' === s);
      if (nodeData.isContainer()) {
        s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CHILDREN_FETCHED);
        if (s) {
          nodeData.setChildrenFetched('true' === s);
        }
      }
    }

    var childNode = xmlNode.getFirstChild();
    if (childNode && childNode.getName() === dvt.AdfDiagramParser._ELEM_EL)
    {
      var arAttrs = childNode.getAttributes();
      var attr;
      var attrs = {};
      for (var i = 0; i < arAttrs.length; i++)
      {
        attr = arAttrs[i];
        attrs[attr.name] = attr.value;
      }
      nodeData.setElAttributes(attrs);
    }
  }
  return nodeData;
};

dvt.AdfDiagramParser.prototype.ParseLinkData = function(xmlNode)
{
  var linkData = new DvtAdfDiagramLinkData();
  var s;

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_ID);
  linkData.setId(s);

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_GROUP_ID);
  linkData.setGroupId(s);

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_TEMPLATE_ID);
  linkData.setTemplateId(s);

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_PARENT_ID);
  linkData.setStartId(s);

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CHILD_ID);
  linkData.setEndId(s);

  var childNode = xmlNode.getFirstChild();
  if (childNode && childNode.getName() === dvt.AdfDiagramParser._ELEM_EL)
  {
    var arAttrs = childNode.getAttributes();
    var attr;
    var attrs = {};
    for (var i = 0; i < arAttrs.length; i++)
    {
      attr = arAttrs[i];
      attrs[attr.name] = attr.value;
    }
    linkData.setElAttributes(attrs);
  }

  s = parseInt(xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_ROW_INDEX));
  linkData.setRowIndex(s);

  return linkData;
};

/**
 * Sets legend data for diagram
 * @param {dvt.XmlNode} xmlNode the xml node containing the legend data
 * @protected
 */
dvt.AdfDiagramParser.prototype.ParseLegend = function(xmlNode) {
  var s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_LEGEND_JSON_DATA);
  if (s) {
    var legendData = eval('(' + s + ')');//From the server produced Json Object @HtmlUpdateOk
    this._diagram.SetLegendJson(legendData);
  }
};

/**
 * Parses attributes related to the overviewWindow
 *
 * @param {dvt.XmlNode} xmlNode the xml node containing the overviewWindow attributes
 */
dvt.AdfDiagramParser.prototype.ParseOverviewWindow = function(xmlNode) {
  var width = 108;
  var height = 81;
  var ovWindow = new dvt.OverviewWindow(this._diagram.getCtx(), 'dgOverviewWindow', 0, 0, width, height);
  ovWindow.loadXmlNode(xmlNode);

  var ovStyleMap = ovWindow.getSkinStyle();
  if (!ovStyleMap)
    ovStyleMap = new Object();

  ovStyleMap[dvt.OverviewWindow.VIEWPORT_BORDER_COLOR] = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_OV_WIN_VIEWPORT_BORDER_COLOR);
  ovStyleMap[dvt.OverviewWindow.VIEWPORT_BG_COLOR] = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_OV_WIN_VIEWPORT_BG_COLOR);
  ovWindow.setSkinStyle(ovStyleMap);

  this._diagram.setOverviewWindow(ovWindow);
};

dvt.AdfDiagramParser.prototype.ParseResources = function(xmlNode) {
  this._diagram.setResources(xmlNode);
};

dvt.AdfDiagramParser.prototype.parseOverviewContentAttrs = function(xmlNode) {
  var ovStyleMap = this._diagram.getOverviewWindowContentStyle();
  if (!ovStyleMap)
    ovStyleMap = new Object();
  ovStyleMap[dvt.AdfDiagram.OVERVIEW_STYLE] = new dvt.CSSStyle(xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_OV_WIN_NODE));
  ovStyleMap[dvt.AdfDiagram.OVERVIEW_CONTAINER_STYLE] = new dvt.CSSStyle(xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_OV_WIN_DISCLOSED_NODE));
  this._diagram.setOverviewWindowContentStyle(ovStyleMap);
};

dvt.AdfDiagramParser.validateColor = function(color) {
  if (! dvt.ColorUtils.isColor(color, false))        // if a defined color such as 'yellow"
    color = dvt.ColorUtils.getColorFromName(color);      // convert to #rrggbb.
  return color;
};



/*-------------------------------------------------------------------------*/
/*   PanelCard                                                             */
/*-------------------------------------------------------------------------*/
dvt.AdfDiagramParser.prototype.ParsePanelCardAttrs = function(xmlNode) {
  var s;

  // general card style
  var cardCSSStyle = new dvt.CSSStyle();
  var bCardStyle = false;
  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CARD_FILL_TYPE);
  if (s) {
    cardCSSStyle.setStyle(dvt.CSSStyle.FILL_TYPE, s);
    bCardStyle = true;
  }
  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CARD_BORDER_TYPE);
  if (s) {
    cardCSSStyle.setStyle(dvt.CSSStyle.BORDER_TYPE, s);
    bCardStyle = true;
  }
  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CARD_BORDER_COLOR);
  if (s) {
    cardCSSStyle.setStyle(dvt.CSSStyle.BORDER_COLOR, s);
    bCardStyle = true;
  }
  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CARD_BACKGROUND_COLOR);
  if (s) {
    cardCSSStyle.setStyle(dvt.CSSStyle.BACKGROUND_COLOR, s);
    bCardStyle = true;
  }
  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CARD_BORDER_RADIUS);
  if (s) {
    cardCSSStyle.setStyle('border-radius', s);
    bCardStyle = true;
  }
  if (bCardStyle)
    this._diagram.setCardStyle(cardCSSStyle);
};

dvt.AdfDiagramParser.prototype.ParsePanelCardHeaderAttrs = function(xmlNode) {
  var s;
  // header styles
  var headerStyles = this._diagram.getCardHeaderStyle();
  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CARD_HEADER_TEXT);
  this.AddCardHeaderStyle(headerStyles, s, 'zoom100');

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CARD_HEADER_TEXT75);
  this.AddCardHeaderStyle(headerStyles, s, 'zoom75');

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CARD_HEADER_TEXT50);
  this.AddCardHeaderStyle(headerStyles, s, 'zoom50');

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CARD_HEADER_TEXT25);
  this.AddCardHeaderStyle(headerStyles, s, 'zoom25');

  this._diagram.setCardHeaderStyle(headerStyles);
};

dvt.AdfDiagramParser.prototype.ParsePanelCardNavButtonAttrs = function(xmlNode) {
  var s;
  //button styles
  var cardNavButtonStyle = new dvt.CSSStyle();
  var bcardNavButtonStyle = false;

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_FILL_TYPE);
  bcardNavButtonStyle = this.AddButtonStyle(cardNavButtonStyle, s, dvt.CSSStyle.FILL_TYPE, bcardNavButtonStyle);

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_BORDER_COLOR);
  bcardNavButtonStyle = this.AddButtonStyle(cardNavButtonStyle, s, dvt.CSSStyle.BORDER_COLOR, bcardNavButtonStyle);

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_ICON_COLOR);
  bcardNavButtonStyle = this.AddButtonStyle(cardNavButtonStyle, s, dvt.CSSStyle.COLOR, bcardNavButtonStyle);

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_BACKGROUND_COLOR);
  bcardNavButtonStyle = this.AddButtonStyle(cardNavButtonStyle, s, dvt.CSSStyle.BACKGROUND_COLOR, bcardNavButtonStyle);

  if (bcardNavButtonStyle) {
    if (this._diagram.getSkinName() == dvt.CSSStyle.SKIN_SKYROS)
      cardNavButtonStyle.setStyle(dvt.CSSStyle.FILL_TYPE, 'solid');
    this._diagram.setCardNavButtonStyle(cardNavButtonStyle);
  }

  cardNavButtonStyle = new dvt.CSSStyle();
  bcardNavButtonStyle = false;

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_HOVER_FILL_TYPE);
  bcardNavButtonStyle = this.AddButtonStyle(cardNavButtonStyle, s, dvt.CSSStyle.FILL_TYPE, bcardNavButtonStyle);

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_HOVER_BORDER_COLOR);
  bcardNavButtonStyle = this.AddButtonStyle(cardNavButtonStyle, s, dvt.CSSStyle.BORDER_COLOR, bcardNavButtonStyle);

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_HOVER_ICON_COLOR);
  bcardNavButtonStyle = this.AddButtonStyle(cardNavButtonStyle, s, dvt.CSSStyle.COLOR, bcardNavButtonStyle);

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_HOVER_BACKGROUND_COLOR);
  bcardNavButtonStyle = this.AddButtonStyle(cardNavButtonStyle, s, dvt.CSSStyle.BACKGROUND_COLOR, bcardNavButtonStyle);

  if (bcardNavButtonStyle) {
    if (this._diagram.getSkinName() == dvt.CSSStyle.SKIN_SKYROS)
      cardNavButtonStyle.setStyle(dvt.CSSStyle.FILL_TYPE, 'solid');
    this._diagram.setCardNavButtonHoverStyle(cardNavButtonStyle);
  }

  cardNavButtonStyle = new dvt.CSSStyle();
  bcardNavButtonStyle = false;

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_ACTIVE_FILL_TYPE);
  bcardNavButtonStyle = this.AddButtonStyle(cardNavButtonStyle, s, dvt.CSSStyle.FILL_TYPE, bcardNavButtonStyle);

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_ACTIVE_ICON_COLOR);
  bcardNavButtonStyle = this.AddButtonStyle(cardNavButtonStyle, s, dvt.CSSStyle.COLOR, bcardNavButtonStyle);

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_ACTIVE_BORDER_COLOR);
  bcardNavButtonStyle = this.AddButtonStyle(cardNavButtonStyle, s, dvt.CSSStyle.BORDER_COLOR, bcardNavButtonStyle);

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_ACTIVE_BACKGROUND_COLOR);
  bcardNavButtonStyle = this.AddButtonStyle(cardNavButtonStyle, s, dvt.CSSStyle.BACKGROUND_COLOR, bcardNavButtonStyle);

  if (bcardNavButtonStyle) {
    if (this._diagram.getSkinName() == dvt.CSSStyle.SKIN_SKYROS)
      cardNavButtonStyle.setStyle(dvt.CSSStyle.FILL_TYPE, 'solid');
    this._diagram.setCardNavButtonActiveStyle(cardNavButtonStyle);
  }

  cardNavButtonStyle = new dvt.CSSStyle();
  bcardNavButtonStyle = false;

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_DISABLED_FILL_TYPE);
  bcardNavButtonStyle = this.AddButtonStyle(cardNavButtonStyle, s, dvt.CSSStyle.FILL_TYPE, bcardNavButtonStyle);

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_DISABLED_BORDER_COLOR);
  bcardNavButtonStyle = this.AddButtonStyle(cardNavButtonStyle, s, dvt.CSSStyle.BORDER_COLOR, bcardNavButtonStyle);

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_DISABLED_ICON_COLOR);
  bcardNavButtonStyle = this.AddButtonStyle(cardNavButtonStyle, s, dvt.CSSStyle.COLOR, bcardNavButtonStyle);

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_DISABLED_BACKGROUND_COLOR);
  bcardNavButtonStyle = this.AddButtonStyle(cardNavButtonStyle, s, dvt.CSSStyle.BACKGROUND_COLOR, bcardNavButtonStyle);

  if (bcardNavButtonStyle) {
    if (this._diagram.getSkinName() == dvt.CSSStyle.SKIN_SKYROS)
      cardNavButtonStyle.setStyle(dvt.CSSStyle.FILL_TYPE, 'solid');
    this._diagram.setCardNavButtonDisabledStyle(cardNavButtonStyle);
  }
};

dvt.AdfDiagramParser.prototype.ParsePanelCardTrainButtonAttrs = function(xmlNode) {
  var s;

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CARD_TRAIN_BUTTON_VISIBILITY);
  if (s && s.toLowerCase() === 'false')
    this._diagram.setCardTrainButtonVisible(false);

  var bcardTrainButtonStyle;
  var bcardNavButtonStyle;

  //alta train icons
  var cardTrainButtonStyle = new dvt.CSSStyle();
  var enaUrl = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CARD_TRAIN_BUTTON_ICON);
  if (enaUrl) {
    bcardNavButtonStyle = true;
    cardTrainButtonStyle.setIconUrl(enaUrl);
  }
  else {
    bcardTrainButtonStyle = false;

    s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CARD_TRAIN_BUTTON_BORDER_COLOR);
    bcardNavButtonStyle = this.AddButtonStyle(cardTrainButtonStyle, s, dvt.CSSStyle.BORDER_COLOR, bcardTrainButtonStyle);

    s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CARD_TRAIN_BUTTON_BACKGROUND_COLOR);
    bcardNavButtonStyle = this.AddButtonStyle(cardTrainButtonStyle, s, dvt.CSSStyle.BACKGROUND_COLOR, bcardTrainButtonStyle);

  }
  if (bcardNavButtonStyle) {
    this._diagram.setCardTrainButtonStyle(cardTrainButtonStyle);
  }

  //alta train hover icons
  cardTrainButtonStyle = new dvt.CSSStyle();
  var hoverUrl = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CARD_TRAIN_BUTTON_HOVER_ICON);
  if (! hoverUrl)
    hoverUrl = enaUrl;
  if (hoverUrl) {
    cardTrainButtonStyle.setIconUrl(hoverUrl);
    this._diagram.setCardTrainButtonHoverStyle(cardTrainButtonStyle);
  }

  //alta train active icons
  cardTrainButtonStyle = new dvt.CSSStyle();
  var activeUrl = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CARD_TRAIN_BUTTON_ACTIVE_ICON);
  if (! activeUrl)
    activeUrl = enaUrl;
  if (activeUrl) {
    bcardNavButtonStyle = true;
    cardTrainButtonStyle.setIconUrl(activeUrl);
  }
  else {
    bcardTrainButtonStyle = false;

    s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CARD_TRAIN_BUTTON_ACTIVE_BORDER_COLOR);
    bcardNavButtonStyle = this.AddButtonStyle(cardTrainButtonStyle, s, dvt.CSSStyle.BORDER_COLOR, bcardTrainButtonStyle);

    s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CARD_TRAIN_BUTTON_ACTIVE_BACKGROUND_COLOR);
    bcardNavButtonStyle = this.AddButtonStyle(cardTrainButtonStyle, s, dvt.CSSStyle.BACKGROUND_COLOR, bcardTrainButtonStyle);

  }
  if (bcardNavButtonStyle) {
    this._diagram.setCardTrainButtonActiveStyle(cardTrainButtonStyle);
  }

};

dvt.AdfDiagramParser.prototype.ParsePanelCardNextPrevButtonAttrs = function(xmlNode) {
  var buttonImages = dvt.AdfDiagramParser.getButtonImages(xmlNode, dvt.AdfDiagramParser._ATTR_CARD_PREV_ICON,
      dvt.AdfDiagramParser._ATTR_CARD_PREV_HOVER_ICON, dvt.AdfDiagramParser._ATTR_CARD_PREV_ACTIVE_ICON);
  if (buttonImages)
    this._diagram.setCardPrevButtonIcon(buttonImages);

  buttonImages = dvt.AdfDiagramParser.getButtonImages(xmlNode, dvt.AdfDiagramParser._ATTR_CARD_NEXT_ICON,
      dvt.AdfDiagramParser._ATTR_CARD_NEXT_HOVER_ICON, dvt.AdfDiagramParser._ATTR_CARD_NEXT_ACTIVE_ICON);
  if (buttonImages)
    this._diagram.setCardNextButtonIcon(buttonImages);
};

dvt.AdfDiagramParser.prototype.AddButtonStyle = function(buttonStyles, style, propName, bModified) {
  if (style) {
    buttonStyles.setStyle(propName, style);
    bModified = true;
  }
  return bModified;
};

dvt.AdfDiagramParser.prototype.AddCardHeaderStyle = function(headerStyles, style, zoomAttr) {
  if (style) {
    var fontCSSStyle = new dvt.CSSStyle();
    fontCSSStyle.parseInlineStyle(style);
    headerStyles[zoomAttr] = fontCSSStyle;
  }
};


dvt.AdfDiagramParser.prototype.ParseAdditionalPanelCardData = function(rootXmlNode) {
  this._diagram.startParsingPanelCardData();

  var pcData = rootXmlNode.getElementsByTagName(dvt.AdfDiagramParser._ELEM_DATA);
  if (pcData && pcData[0]) {
    this._parsePanelCardData(pcData[0]);
  }

  this._diagram.stopParsingPanelCardData();
};


/**
 * Parse the data section of the XML.
 *
 * @param xmlNode node to parse
 */
dvt.AdfDiagramParser.prototype._parsePanelCardData = function(xmlNode) {
  var childNodes = xmlNode.getChildNodes();
  for (var i = 0; i < childNodes.length; i++) {
    var child = childNodes[i];
    if (child && child.getName() == dvt.AdfDiagramParser._ELEM_NODE_DATA) {
      this._parsePanelCardItemData(child);
    }
  }
};


dvt.AdfDiagramParser.prototype._parsePanelCardItemData = function(xmlNode) {
  var s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_ID);

  if (s) {
    var childNodes = xmlNode.getChildNodes();
    if (childNodes) {
      var child = childNodes[0];
      if (child && child.getName() == dvt.AdfDiagramParser._ELEM_EL)
        this._diagram.addPanelCardData(s, child.getAttributes());
    }
  }
};


/**
 * Parse the <cards> node.
 *
 * @param view TopologyView being created
 * @param node XMLNode to parse
 */
dvt.AdfDiagramParser.prototype.ParseCards = function(xmlNode) {
  //defer panelCard loading
  var s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_NODE_SDI_MAP);
  if (s) {
    this._diagram.setNodeShowDetailItemIndexMap(s);
  }

  //Bug 14534257 - HTML5: panelCard of expanded child node didn't sync to parent
  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_NEW_NODE_SDI_MAP);
  if (s) {
    this._diagram.setShowDetailItems(s);
  }

  //Bug 19270204 - diagram panel card nav buttons are not aligned
  //Use the skin name to position the panel card navigation buttons
  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_SKIN);
  if (s) {
    this._diagram.setCardNavButtonSkin(s);
  }

  var cardList = new Array();
  var childNodes = xmlNode.getChildNodes();

  for (var i = 0; i < childNodes.length; i++) {
    var child = childNodes[i];
    if (child && child.getName() == dvt.AdfDiagramParser._ELEM_CARD) {
      cardList.push([child.getAttr(dvt.AdfDiagramParser._ATTR_CARD_TEXT),
                     child.getAttr(dvt.AdfDiagramParser._ATTR_CARD_ICON),
                     child.getAttr(dvt.AdfDiagramParser._ATTR_CARD_ID)
                    ]);
    }
  }
  if (cardList.length > 0)
    this._diagram.setCardSelectorList(cardList);

};

dvt.AdfDiagramParser.prototype.setPanelCardStyles = function() {

  var styleList = dvt.AfStyleUtils.getDefaultPanelCardStyles();

  // general styles and header
  var styles = styleList[0];
  var skinStyles = this._diagram.getCardStyle();
  styles.merge(skinStyles);

  //array order is important here - panel card relies on style indexes

  //nav buttons
  styleList.push(this._diagram.getCardNavButtonStyle());
  styleList.push(this._diagram.getCardNavButtonHoverStyle());
  styleList.push(this._diagram.getCardNavButtonActiveStyle());

  //header text
  styleList.push(this._diagram.getCardHeaderStyle()['zoom100']);
  styleList.push(this._diagram.getCardHeaderStyle()['zoom75']);
  var images = this._diagram.getCardPrevButtonIcon();
  var ix = 0;
  if (images) {
    styleList.push(images[0]);
    styleList.push(images[1]);
    styleList.push(images[2]);
  }
  else
    for (ix = 0; ix < 3; ix++)
      styleList.push(null);

    images = this._diagram.getCardNextButtonIcon();
  if (images) {
    styleList.push(images[0]);
    styleList.push(images[1]);
    styleList.push(images[2]);
  }
  else
    for (ix = 0; ix < 3; ix++)
      styleList.push(null);

    //train buttons
    styleList.push(this._diagram.getCardTrainButtonStyle());
  styleList.push(this._diagram.getCardTrainButtonHoverStyle());
  styleList.push(this._diagram.getCardTrainButtonActiveStyle());

  dvt.AfStyleUtils.setPanelCardDefault(styleList);
};

dvt.AdfDiagramParser.getButtonImages = function(xmlNode, ena, hov, act) {
  var imageUrls = [];
  var defImgUrl = xmlNode.getAttr(ena);
  var hovImgUrl = xmlNode.getAttr(hov) ? xmlNode.getAttr(hov) : defImgUrl;
  var actImgUrl = xmlNode.getAttr(act) ? xmlNode.getAttr(act) : defImgUrl;

  if (!defImgUrl && !hovImgUrl && !actImgUrl)
    return null;
  else {
    imageUrls.push(defImgUrl);
    imageUrls.push(hovImgUrl);
    imageUrls.push(actImgUrl);
  }
  return imageUrls;
};

dvt.AdfDiagramParser.prototype.parseStackingStyles = function(xmlNode) {
  var s;
  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_STACK_PREVIEW_COLOR);
  if (s && (s = dvt.AdfDiagramParser.validateColor(s)))
    this._diagram.setStackPreviewColor(s);

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_STACK_BADGE_STYLE);
  if (s)
    this._diagram.setStackBadgeStyles(new dvt.CSSStyle(s));

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_UNDERLAY_STYLE);
  if (s)
    this._diagram.setStackUnderlayStyles(new dvt.CSSStyle(s));
};


dvt.AdfDiagramParser.prototype.parseStackPreview = function(xmlNode) {
  var stackPreviewPanZoom = {};
  var setPanZoom = false;

  var s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_PREVIEW_NODE);
  if (s)
    this._diagram._stackPreviewNode = s;

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_PREVIEW_PAN_X);
  if (s) {
    stackPreviewPanZoom.panX = parseFloat(s);
    setPanZoom = true;
  }
  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_PREVIEW_PAN_Y);
  if (s) {
    stackPreviewPanZoom.panY = parseFloat(s);
    setPanZoom = true;
  }
  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_PREVIEW_ZOOM);
  if (s) {
    stackPreviewPanZoom.zoom = parseFloat(s);
    setPanZoom = true;
  }
  if (setPanZoom) {
    this._diagram._stackPreviewPanZoom = stackPreviewPanZoom;
  }
};


/**
 * Parses styles for Diagram buttons
 * @param {dvt.XmlNode} xmlNode The xml node containing button styles
 */
dvt.AdfDiagramParser.prototype.parseDiagramButtonStyles = function(xmlNode) {
  var s;

  var cssStyleEna = new dvt.CSSStyle();
  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_BUTTON_STYLE_ENA);
  if (s)
    cssStyleEna.parseInlineStyle(s);

  var cssStyleOvr;
  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_BUTTON_STYLE_OVR);
  if (s)
    cssStyleOvr = new dvt.CSSStyle(s);
  else
    cssStyleOvr = cssStyleEna.clone();

  var cssStyleDwn;
  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_BUTTON_STYLE_DWN);
  if (s)
    cssStyleDwn = new dvt.CSSStyle(s);
  else
    cssStyleDwn = cssStyleEna.clone();

  var buttonStyles = [];
  buttonStyles[dvt.AdfDiagram.BUTTON_STATE_ENA] = cssStyleEna;
  buttonStyles[dvt.AdfDiagram.BUTTON_STATE_OVR] = cssStyleOvr;
  buttonStyles[dvt.AdfDiagram.BUTTON_STATE_DWN] = cssStyleDwn;
  this._diagram.setDiagramButtonStyles(buttonStyles);
};


dvt.AdfDiagramParser.prototype.parseBreadcrumbsAttrs = function(xmlNode) {
  var breadcrumbsStyle = dvt.AdfDiagramParser.getBreadcrumbsStyles(xmlNode, dvt.AdfDiagramParser._ATTR_BREADCRUMBS_STYLE,
      dvt.AdfDiagramParser._ATTR_BREADCRUMBS_HOVER_STYLE,
      dvt.AdfDiagramParser._ATTR_BREADCRUMBS_ACTIVE_STYLE,
      dvt.AdfDiagramParser._ATTR_BREADCRUMBS_SELECTED_STYLE);
  if (breadcrumbsStyle)
    this._diagram.setBreadcrumbsStyles(breadcrumbsStyle);
};


/**
 * Parses styles for Diagram breadbrumb buttons
 * @param {dvt.XmlNode} xmlNode The xml node containing breadcrumb styles
 * @param {string} ena An attribute for enabled style
 * @param {string} hov An attribute for hovered style
 * @param {string} act An attribute for active style
 * @param {string} dis An attribute for disabled style
 *
 * @return {array} styles An array of styles
 */
dvt.AdfDiagramParser.getBreadcrumbsStyles = function(xmlNode, ena, hov, act, sel) {
  // parse skin
  var enaSkinStyles = xmlNode.getAttr(ena);

  var hovSkinStyles = xmlNode.getAttr(hov) ? xmlNode.getAttr(hov) : enaSkinStyles;
  var actSkinStyles = xmlNode.getAttr(act) ? xmlNode.getAttr(act) : enaSkinStyles;
  var selSkinStyles = xmlNode.getAttr(sel) ? xmlNode.getAttr(sel) : enaSkinStyles;

  // since the state can contain partial styles, use enabled style as a base
  hovSkinStyles = dvt.AdfDiagramParser.mergeStyles(enaSkinStyles, hovSkinStyles);
  actSkinStyles = dvt.AdfDiagramParser.mergeStyles(enaSkinStyles, actSkinStyles);
  selSkinStyles = dvt.AdfDiagramParser.mergeStyles(enaSkinStyles, selSkinStyles);

  var styles = [];
  styles[dvt.AdfDiagram.BUTTON_STATE_ENA] = enaSkinStyles;
  styles[dvt.AdfDiagram.BUTTON_STATE_OVR] = hovSkinStyles;
  styles[dvt.AdfDiagram.BUTTON_STATE_DWN] = actSkinStyles;
  styles[dvt.AdfDiagram.BUTTON_STATE_DIS] = selSkinStyles;

  return styles;
};

dvt.AdfDiagramParser.mergeStyles = function(baseStyle, skinStyle) {
  if (!baseStyle)
    return null;
  if (baseStyle == skinStyle)
    return baseStyle;

  var mergedStyle = baseStyle;
  if (skinStyle) {
    var styleCss = new dvt.CSSStyle(baseStyle);
    styleCss.parseInlineStyle(skinStyle);
    mergedStyle = styleCss.toString();
  }
  return mergedStyle;
};


/**
 * Parses attributes related to diagram node styles
 * @param {dvt.XmlNode} xmlNode The xml node to parse
 * @protected
 */
dvt.AdfDiagramParser.prototype.ParseNodeStyles = function(xmlNode) {
  var nodeStyles = {};

  var setNodeStyle = false;

  var s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_NODE_SELECT_COLOR);
  if (s && (s = dvt.AdfDiagramParser.validateColor(s))) {
    setNodeStyle = true;
    nodeStyles[dvt.AdfDiagramNode.SELECTED_COLOR] = s;
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_NODE_HOVER_INNER_COLOR);
  if (s && (s = dvt.AdfDiagramParser.validateColor(s))) {
    setNodeStyle = true;
    nodeStyles[dvt.AdfDiagramNode.HOVER_INNER_COLOR] = s;
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_NODE_HOVER_OUTER_COLOR);
  if (s && (s = dvt.AdfDiagramParser.validateColor(s))) {
    setNodeStyle = true;
    nodeStyles[dvt.AdfDiagramNode.HOVER_OUTER_COLOR] = s;
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_NODE_CONTAINER_STYLE);
  if (s) {
    setNodeStyle = true;
    nodeStyles[dvt.AdfDiagramNode.CONTAINER_STYLE] = new dvt.CSSStyle(s);
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_NODE_LABEL_STYLE);
  if (s) {
    setNodeStyle = true;
    nodeStyles[dvt.AdfDiagramNode.LABEL_STYLE] = new dvt.CSSStyle(s);
  }

  if (setNodeStyle) {
    this._diagram.setNodeStyles(nodeStyles);
  }
};


/**
 * Parses attributes related to diagram link styles
 * @param {dvt.XmlNode} xmlNode The xml node to parse
 * @protected
 */
dvt.AdfDiagramParser.prototype.ParseLinkStyles = function(xmlNode) {
  var linkStyles = {};

  var setLinkStyle = false;

  var s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_LINK_COLOR);
  if (s && (s = dvt.AdfDiagramParser.validateColor(s))) {
    setLinkStyle = true;
    linkStyles[dvt.AdfDiagramLink.COLOR] = s;
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_LINK_SELECT_COLOR);
  if (s && (s = dvt.AdfDiagramParser.validateColor(s))) {
    setLinkStyle = true;
    linkStyles[dvt.AdfDiagramLink.SELECTED_COLOR] = s;
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_LINK_HOVER_INNER_COLOR);
  if (s && (s = dvt.AdfDiagramParser.validateColor(s))) {
    setLinkStyle = true;
    linkStyles[dvt.AdfDiagramLink.HOVER_INNER_COLOR] = s;
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_LINK_HOVER_OUTER_COLOR);
  if (s && (s = dvt.AdfDiagramParser.validateColor(s))) {
    setLinkStyle = true;
    linkStyles[dvt.AdfDiagramLink.HOVER_OUTER_COLOR] = s;
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_LINK_LABEL_STYLE);
  if (s) {
    setLinkStyle = true;
    linkStyles[dvt.AdfDiagramLink.LABEL_STYLE] = new dvt.CSSStyle(s);
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_LINK_PROMOTED_COLOR);
  if (s && (s = dvt.AdfDiagramParser.validateColor(s))) {
    setLinkStyle = true;
    linkStyles[dvt.AdfDiagramLink.PROMOTED_COLOR] = s;
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_LINK_PROMOTED_STYLE);
  if (s) {
    setLinkStyle = true;
    s = dvt.AdfDiagramParser._LINK_STYLE_CONVERTER[s];
    linkStyles[dvt.AdfDiagramLink.PROMOTED_STYLE] = s ? s : dvt.AdfDiagramLinkDef.LINK_STYLE_DOT;
  }

  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_LINK_PROMOTED_WIDTH);
  if (s && !isNaN(parseInt(s))) {
    setLinkStyle = true;
    linkStyles[dvt.AdfDiagramLink.PROMOTED_WIDTH] = s;
  }

  if (setLinkStyle) {
    this._diagram.setLinkStyles(linkStyles);
  }
};


/**
 * Parse XML attributes related to button style
 * @protected
 * @param {dvt.XmlNode} xmlNode The xml node to parse
 */
dvt.AdfDiagramParser.prototype.ParseButtonStyle = function(xmlNode) {
  var buttonStyleList = [];
  var labelStyleList = [];

  //parse button container attributes
  this.ParseButtonContainerStyle(xmlNode, buttonStyleList, dvt.AdfDiagramParser._ATTR_ENABLED_STATE);
  this.ParseButtonContainerStyle(xmlNode, buttonStyleList, dvt.AdfDiagramParser._ATTR_HOVER_STATE);
  this.ParseButtonContainerStyle(xmlNode, buttonStyleList, dvt.AdfDiagramParser._ATTR_ACTIVE_STATE);
  this._diagram.setButtonStyle(buttonStyleList);

  //parse button label attributes
  this.ParseButtonLabelStyle(xmlNode, labelStyleList, dvt.AdfDiagramParser._ATTR_ENABLED_STATE);
  this.ParseButtonLabelStyle(xmlNode, labelStyleList, dvt.AdfDiagramParser._ATTR_HOVER_STATE);
  this.ParseButtonLabelStyle(xmlNode, labelStyleList, dvt.AdfDiagramParser._ATTR_ACTIVE_STATE);
  this._diagram.setLabelStyle(labelStyleList);
};


/**
 * Parse XML attributes related to button container style
 * @protected
 * @param {dvt.XmlNode} xmlNode The xml node to parse
 * @param {Array} buttonStyleList  Array of button styles for each state
 * @param {string} state  button style for the state
 */
dvt.AdfDiagramParser.prototype.ParseButtonContainerStyle = function(xmlNode, buttonStyleList, state) {
  var s;
  var buttonStyle = new dvt.CSSStyle();
  var bButtonStyle = false;

  //button container
  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_BUTTON_CONTAINER + state + dvt.AdfDiagramParser._ATTR_BUTTON_FILL_TYPE);
  if (s) {
    bButtonStyle = this.AddButtonStyle(buttonStyle, s, dvt.CSSStyle.FILL_TYPE, bButtonStyle);
  }
  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_BUTTON_CONTAINER + state + dvt.AdfDiagramParser._ATTR_BUTTON_BACKGROUND_COLOR);
  if (s) {
    bButtonStyle = this.AddButtonStyle(buttonStyle, s, dvt.CSSStyle.BACKGROUND_COLOR, bButtonStyle);
  }
  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_BUTTON_CONTAINER + state + dvt.AdfDiagramParser._ATTR_BUTTON_BACKGROUND);
  if (s) {
    bButtonStyle = this.AddButtonStyle(buttonStyle, s, dvt.CSSStyle.BACKGROUND, bButtonStyle);
  }
  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_BUTTON_CONTAINER + state + dvt.AdfDiagramParser._ATTR_BUTTON_BORDER_COLOR);
  if (s) {
    bButtonStyle = this.AddButtonStyle(buttonStyle, s, dvt.CSSStyle.BORDER_COLOR, bButtonStyle);
  }

  if (bButtonStyle)
    buttonStyleList.push(buttonStyle);
  else
    buttonStyleList.push(null);
};


/**
 * Parse XML attributes related to button label style
 * @protected
 * @param {dvt.XmlNode} xmlNode The xml node to parse
 * @param {Array} labelStyleList  Array of button label style
 * @param {string} state  label style for button state
 */
dvt.AdfDiagramParser.prototype.ParseButtonLabelStyle = function(xmlNode, labelStyleList, state) {
  var s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_BUTTON_LABEL + state);
  if (s) {
    var labelStyle = new dvt.CSSStyle();
    labelStyle.parseInlineStyle(s);
    labelStyleList.push(labelStyle);
  } else {
    labelStyleList.push(null);
  }
};


/**
 * Parses palette related attributes
 * @param {dvt.XmlNode} xmlNode The xml node to parse
 */
dvt.AdfDiagramParser.prototype.parsePaletteAttrs = function(xmlNode) {
  var s;

  var cssStyleEna = new dvt.CSSStyle();
  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_PALETTE_SECTION_STYLE);
  if (s)
    cssStyleEna.parseInlineStyle(s);

  var cssStyleOvr;
  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_PALETTE_SECTION_HOVER_STYLE);
  if (s)
    cssStyleOvr = new dvt.CSSStyle(s);
  else
    cssStyleOvr = cssStyleEna.clone();

  var cssStyleDwn;
  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_PALETTE_SECTION_ACTIVE_STYLE);
  if (s)
    cssStyleDwn = new dvt.CSSStyle(s);
  else
    cssStyleDwn = cssStyleEna.clone();

  var paletteStyleMap = new Object();
  paletteStyleMap['skin'] = this._diagram.getSkinName();
  paletteStyleMap['sectionHeader'] = new Object();
  paletteStyleMap['sectionHeader']['styleEna'] = cssStyleEna;
  paletteStyleMap['sectionHeader']['styleOvr'] = cssStyleEna;
  paletteStyleMap['sectionHeader']['styleDwn'] = cssStyleEna;


  var cssItemStyle = new dvt.CSSStyle();
  s = xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_PALETTE_ITEM_STYLE);
  if (s) {
    cssItemStyle.parseInlineStyle(s);
    paletteStyleMap['sectionItem'] = new Object();
    paletteStyleMap['sectionItem']['style'] = cssItemStyle;
  }
  this._diagram.setPaletteStyles(paletteStyleMap);
};

/**
 * Parses scrollbar styles used by the search results panel
 * @param {dvt.XmlNode} xmlNode The xml node to parse
 */
dvt.AdfDiagramParser.prototype.parseScrollbarStylesAttrs = function(xmlNode) {
  var sbStyleMap = new Object();
  // scrollbar styles for scrollable container
  dvt.StyleUtils.setStyle(sbStyleMap, DvtScrollbar.BACKGROUND, xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_SCROLLBAR_BACKGROUND));
  dvt.StyleUtils.setStyle(sbStyleMap, DvtScrollbar.BORDER_COLOR, xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_SCROLLBAR_BORDER_COLOR));
  dvt.StyleUtils.setStyle(sbStyleMap, DvtScrollbar.HANDLE_COLOR, xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_SCROLLBAR_HANDLE_COLOR));
  dvt.StyleUtils.setStyle(sbStyleMap, DvtScrollbar.HANDLE_ACTIVE_COLOR, xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_SCROLLBAR_HANDLE_ACTIVE_COLOR));
  dvt.StyleUtils.setStyle(sbStyleMap, DvtScrollbar.HANDLE_HOVER_COLOR, xmlNode.getAttr(dvt.AdfDiagramParser._ATTR_SCROLLBAR_HANDLE_HOVER_COLOR));
  var styles = dvt.JsonUtils.merge(sbStyleMap, this._diagram.getSubcomponentStyles());
  this._diagram.setSubcomponentStyles(styles);
};
// Copyright (c) 2014, 2023, Oracle and/or its affiliates.

/**
 * @constructor
 * AdfDiagram JSON Parser
 * @param {dvt.AdfDiagramParser} diagramParser Diagram parser
 * @param {dvt.AdfDiagram} diagram  Diagram object
 */
dvt.AdfDiagramJsonParser = function(parser, diagram)
{
  this.Init(parser, diagram);
};

dvt.Obj.createSubclass(dvt.AdfDiagramJsonParser, dvt.Obj, 'dvt.AdfDiagramJsonParser');


/**
 * Initialize
 * @param {dvt.AdfDiagramParser} diagramParser Diagram parser
 * @param {dvt.AdfDiagram} diagram  Diagram object
 * @protected
 */
dvt.AdfDiagramJsonParser.prototype.Init = function(diagramParser, diagram)
{
  this._diagramParser = diagramParser;
  this._diagram = diagram;
};

/**
 * Load JSON object
 * @param {object} jsonObj  JSON object to load
 * @param {object} paramKeys  Parameter keys
 * @param {object} paramValues  Parameter Values
 */
dvt.AdfDiagramJsonParser.prototype.loadJsonInitial = function(jsonObj, paramKeys, paramValues) {
  var diagramJsonNode = jsonObj;
  var diagramEventNode = jsonObj[dvt.AdfDiagramParser._ELEM_DIAGRAM_EVENT];
  if (diagramEventNode) {
    var event = diagramEventNode[dvt.AdfDiagramParser._ATTR_EVENT_TYPE];
    this._diagram._eventType = event;
    try {
      if (dvt.AdfDiagramParser._SEARCH_EVENT == event) {
        var clientRowKey = diagramEventNode['clientRowKey'];
        var searchText = diagramEventNode['searchText'];
        var searchType = dvt.AdfDiagramParser._SEARCH_EVENT_TYPE_NEXT;
        this._diagram.LoadSearchResultsDataJson(diagramJsonNode, searchText, searchType, clientRowKey);
      }
      else if (dvt.AdfDiagramParser._FETCH_EVENT == event) {
        this.LoadDiagramElement(diagramJsonNode);
        var nodeId = diagramEventNode['clientRowKey'];
        var subtype = diagramEventNode['subtype'];
        this._diagram.getNodeById(nodeId).getData().setChildrenFetched(true);
        this._diagram._resetTransitionState(); // ensure TransitionUtils starts from scratch because there are new nodes/links to account for
        if (dvt.AdfDiagramFetchEvent.DRILL === subtype) {
          this._diagram.drillNode(nodeId);
        }
        else { // DISCLOSE
          this._diagram.setNodeDisclosed(nodeId, true, false, true);
        }
        this._diagram.setInteractionEnabled(true);
      }
      else if (dvt.AdfDiagramParser._PANELCARD_FETCH_EVENT == event) {
        var clientRowKey = diagramEventNode['clientRowKey'];
        var nextCard = diagramEventNode['nextc'];
        var hoverWindow = diagramEventNode['pchw'];
        this.ParseAdditionalPanelCardData(diagramJsonNode);
        this._diagram.applyPanelCardData(clientRowKey, nextCard, hoverWindow);
      }
    }
    finally {
      this._diagram._eventType = null;
    }
    this._diagram._handledEventXml = true;
  }
  else if (jsonObj[dvt.AdfDiagramParser.ATTR_DATA_PPR]) {
    var data = jsonObj[dvt.AdfDiagramParser._ELEM_DATA];
    if (data) {
      // Parse Node and Link Data
      var nodeData = data[dvt.AdfDiagramParser._ELEM_NODE_DATA];
      if (nodeData) {
        for (var i = 0; i < nodeData.length; i++) {
          var node = this._diagram.getNodeById(nodeData[i][dvt.AdfDiagramParser._ATTR_ID]);
          if (node) {
            this.parseNode(nodeData[i], node.getData(), this._diagramParser);
            node.setNodeDef(node.getNodeDef());
            node.FullRenderNode(true);
          }
        }
      }
      var linkData = data[dvt.AdfDiagramParser._ELEM_LINK_DATA];
      if (linkData) {
        for (var i = 0; i < linkData.length; i++) {
          var link = this._diagram.getLinkbyId(linkData[i][dvt.AdfDiagramParser._ATTR_ID]);
          if (link) {
            this.parseLink(linkData[i], link.getData(), this._diagramParser);
            link.setLinkDef(link.getLinkDef());
            link.renderLink();
          }
        }
      }
    }
    this._diagram._handledEventXml = true;
  }
  else {
    this._diagram.resetInteractionEnabled();
    this.LoadDiagramElement(diagramJsonNode);
  }
};


/**
 * Loads the Diagram object
 * @param {object} jsonObj object to load diagram from
 * @protected
 */
dvt.AdfDiagramJsonParser.prototype.LoadDiagramElement = function(diagramNode) {
  if (this.ParseDiagramNodeJson(diagramNode)) {
    this.ParseJsonAttributes(diagramNode);
  }
};


/**
 * Parses JSON attributes (templates, data).
 * @param {object} jsonObj The JSON object to parse
 * @protected
 */
dvt.AdfDiagramJsonParser.prototype.ParseJsonAttributes = function(jsonObj) {
  var s;
  if (!jsonObj)
    return;

  //Note: must parse af selectors and styles before template
  var afSelectors = jsonObj[dvt.AdfDiagramParser._ELEM_SELECTORS];
  var afStyles = jsonObj[dvt.AdfDiagramParser._ELEM_AF_STYLES];

  //save selectors and styles
  if (afSelectors && afStyles) {
    dvt.AfStyleUtils.setTagSkinKeyMapJSON(afSelectors, afStyles);
  }

  //custom Styles if any
  var ctStyles = jsonObj[dvt.AdfDiagramParser._ELEM_CT_STYLES];
  if (ctStyles) {
    var ctStylesAttrs = ctStyles[0].getAttributes();
    //console.log(ctStylesAttrs);
  }

  this._diagramParser.setPanelCardStyles();

  this.ParseDiagramNodeChild(jsonObj);

  // After all the nodes and links have been parsed, parse the isolation state
  this.ParseDiagramIsolation(jsonObj);
};


/**
 * Parses the Diagram attributes
 * @param {object} jsonObj  JSON Object to parse
 * @protected
 */
dvt.AdfDiagramJsonParser.prototype.ParseDiagramNodeChild = function(jsonObj) {
  var templates = jsonObj[dvt.AdfDiagramParser._ELEM_TEMPLATES];
  // Parse Node and Link Templates
  if (templates) {
    var s = templates[dvt.AdfDiagramParser._ELEM_NODE];
    if (s) {
      this.parseNodeTemplates(s, this._diagramParser);
    }
    s = templates[dvt.AdfDiagramParser._ELEM_LINK];
    if (s) {
      this.parseLinkTemplates(s, this._diagramParser);
    }
  }

  var data = jsonObj[dvt.AdfDiagramParser._ELEM_DATA];
  if (data) {
    // Parse Node and Link Data
    var nodeData = data[dvt.AdfDiagramParser._ELEM_NODE_DATA];

    var linkData = data[dvt.AdfDiagramParser._ELEM_LINK_DATA];

    if (nodeData || linkData) {
      this._diagram.startParsingData();
      if (nodeData) {
        this.parseNodeData(nodeData, this._diagramParser);
      }
      if (linkData) {
        this.parseLinkData(linkData, this._diagramParser);
      }
      var cardData = jsonObj[dvt.AdfDiagramParser._ELEM_CARDS];
      if (cardData) {
        this.parseCards(cardData, this);
      }
      this._diagram.stopParsingData();
    }
  }

  //Parse show popup behaviors
  var showPopupBehavior = jsonObj[dvt.AdfDiagramParser._ELEM_SHOW_POPUP_BEHAVIOR];
  if (showPopupBehavior) {
    var behaviors = showPopupBehavior['children'];
    if (behaviors) {
      for (var j = 0; j < behaviors.length; j++) {
        var bhvr = dvt.AfComponentFactory.parseJsonElement(behaviors[j]);
        if (bhvr && behaviors[j]['component'] == dvt.AdfDiagramParser._ELEM_SHOW_POPUP_BEHAVIOR) {
          this._diagram.addShowPopupBehavior(bhvr);
        }
      }
    }
  }

  //Parse Context Menu
  var menuData = jsonObj[dvt.AdfDiagramParser._ELEM_CONTEXT_MENU];
  if (menuData) {
    // Make sure the handler is created
    this._diagram._contextMenuHandler = new dvt.ContextMenuHandler(this._diagram.getCtx(), menuData);
    this._diagram.getEventManager().setContextMenuHandler(this._diagram._contextMenuHandler);
  }

  var legendData = jsonObj[dvt.AdfDiagramParser._ELEM_LEGEND];
  if (legendData) {
    this._diagram.SetLegendJson(legendData);
  }

  var overviewData = jsonObj[dvt.AdfDiagramParser._ELEM_OVERVIEWWINDOW];
  if (overviewData) {
    this.ParseOverviewWindow(overviewData, this);
  }

  var paletteData = jsonObj[dvt.AdfDiagramParser._ELEM_PALETTE];
  if (paletteData) {
    this._diagram.SetPaletteJson(paletteData);
  }

  var searchResultsTemplate = jsonObj[dvt.SearchPanel.ELEM_SEARCH_RESULTS_TEMPLATE];
  var searchAdvancedTemplate = jsonObj[dvt.SearchPanel.ELEM_SEARCH_ADVANCED_TEMPLATE];
  var searchResultsData = jsonObj[dvt.SearchPanel.ELEM_SEARCH_RESULTS_DATA];
  if (searchResultsTemplate || searchAdvancedTemplate || searchResultsData) {
    this._diagram.getSearchPanel(true).parseNodeJson(jsonObj);
  }
};

/**
 * Parses diagram-level attributes from the JSON object.
 * @param {object} jsonObj The JSON object to parse
 * @protected
 */
dvt.AdfDiagramJsonParser.prototype.ParseDiagramNodeJson = function(jsonObj)
{
  var s;

  //Bug 13704008: parse the inlineStyle on the component tag
  this._diagram.parseComponentJson(jsonObj);
  this.parseScrollbarStylesJson(jsonObj);
  this.parseOverviewContentJson(jsonObj);
  this.parseStackingStylesJson(jsonObj);
  this.parseStackPreviewJson(jsonObj);
  this.parseDiagramButtonStylesJson(jsonObj);
  this.parseSelectionJson(jsonObj);
  this.parseBreadcrumbsJson(jsonObj);
  this.parsePaletteJson(jsonObj);

  // node, link skinning
  this.ParseNodeStylesJson(jsonObj);
  this.ParseLinkStylesJson(jsonObj);
  var layoutMap = jsonObj[dvt.AdfDiagramParser._ATTR_CLIENT_LAYOUTS];
  for (var key in layoutMap) {
    try 
    {    
      //From the server produced Json Object @HtmlUpdateOK
      //BUG 34184877 - MAKE DVT COMPATIBLE WITH CSP STANDARDS
      var funcString = layoutMap[key]['strMethod'];
      var func = window;
      var funcSplit = funcString.split('.');
      for(i = 0;i < funcSplit.length;i++)
      {
        func = func[funcSplit[i]];
      }    
      var proxyFunc = function anonymous(funcDef) {
        return function anonymous(layoutContext) {
                  funcDef(layoutContext);
            }
      }(func);
      this._diagram.getLayoutCallback().addLayout(key, proxyFunc);
    }
    catch (e) {
      if (e.name == 'ReferenceError') {
        this._diagram.Log(e.name + ':' + e.message, '2'); // SEVERE
      }
    }
    if (layoutMap[key]['layoutAttrs']) {
      this._diagram.setLayoutAttributes(key, layoutMap[key]['layoutAttrs']);
    }
  }

  s = jsonObj[dvt.AdfDiagramParser._ATTR_SUMMARY];
  if (s)
    this._diagram.setSummary(s);

  s = jsonObj[dvt.AdfDiagramParser._ATTR_LAYOUT];
  if (s)
    this._diagram.setLayout(s);

  s = jsonObj[dvt.AdfDiagramParser._ATTR_INIT_ANIM];
  if (s) {
    if (s == 'auto') {
      s = dvt.BlackBoxAnimationHandler.ALPHA_FADE;
    }
    this._diagram.setInitAnim(s);
  }

  s = jsonObj[dvt.AdfDiagramParser._ATTR_DATA_CHANGE_ANIM];
  if (s) {
    this._diagram.setDataChangeAnim(s);
  }

  s = jsonObj[dvt.AdfDiagramParser._ATTR_ANIMATION_DURATION];
  if (!isNaN(s))
    this._diagram.setAnimationDuration(parseInt(s) / 1000);

  s = jsonObj[dvt.AdfDiagramParser._ATTR_NODE_HIGHLIGHT_BEHAVIOR];
  if (s)
    this._diagram.setNodeHighlightBehavior(s);

  s = jsonObj[dvt.AdfDiagramParser._ATTR_LINK_HIGHLIGHT_BEHAVIOR];
  if (s)
    this._diagram.setLinkHighlightBehavior(s);

  s = jsonObj[dvt.AdfDiagramParser._ATTR_HIGHLIGHTED_NODES];
  if (s)
    this._diagram._highlightedNodes = s.split(',');
  else
    this._diagram._highlightedNodes = null;


  s = jsonObj[dvt.AdfDiagramParser._ATTR_HIGHLIGHTED_LINKS];
  if (s)
    this._diagram._highlightedLinks = s.split(',');
  else
    this._diagram._highlightedLinks = null;

  s = jsonObj[dvt.AdfDiagramParser._ATTR_DISCLOSED_NODES];
  if (s)
    this._diagram._disclosedNodes = s.split(',');
  else
    this._diagram._disclosedNodes = null;

  s = jsonObj[dvt.AdfDiagramParser._ATTR_LAYOUT_BEHAVIOR];
  this._diagram._layoutBehavior = s ? s : 'container';

  s = jsonObj[dvt.AdfDiagramParser._ATTR_ATTR_GRPS];
  if (s)
  {
    //this._diagram.setAttributeGroups(s.split(dvt.AdfDiagramParser._SEMICOLON));
    this._diagram.setAttributeGroups(s.split(/[\s;]+/));
  }

  s = jsonObj[dvt.AdfDiagramParser._ATTR_UNGROUPED];
  if (s)
    this._diagram.setExplodedStacks(s.split(','));

  s = jsonObj[dvt.AdfDiagramParser._ATTR_HIDDEN];
  if (s)
    this._diagram.setHiddenAttributeGroups(s.split(','));

  s = jsonObj[dvt.AdfDiagramParser._ATTR_ROLLOVER_BEHAVIOR];
  if (s)
    this._diagram.setRolloverBehavior(s);

  s = jsonObj[dvt.AdfDiagramParser._ATTR_AF_READ_ONLY];
  this._diagram.setReadOnly(s != false);

  s = jsonObj[dvt.AdfDiagramParser._ATTR_AF_HAS_DRAG_SOURCE];
  if (s)
    this._diagram.setHasDragSource(s);

  s = jsonObj[dvt.AdfDiagramParser._ATTR_MAX_ZOOM];
  if (!isNaN(s)) {
    var maxZoom = 2.0;
    var f = parseFloat(s);
    if (f > 0) {
      maxZoom = f;
    }
    this._diagram.setMaxZoom(maxZoom);
  }

  s = jsonObj[dvt.AdfDiagramParser._ATTR_MIN_ZOOM];
  if (!isNaN(s)) {
    var minZoom = 0.0;
    var f = parseFloat(s);
    if (f > 0) {
      minZoom = Math.min(f, maxZoom);
    }
    this._diagram.setMinZoom(minZoom);
  }

  s = jsonObj[dvt.AdfDiagramParser._ATTR_CONTROL_PANEL_BEHAVIOR];
  if (s)
    this._diagram.setControlPanelBehavior(s);

  var displayedControls = this._diagram.getDisplayedControls();

  s = jsonObj[dvt.AdfDiagramParser._ATTR_PANNING];
  var panningEnabled = 'none' != s;
  this._diagram.SetPanningEnabled(panningEnabled);
  if (panningEnabled) {
    displayedControls = displayedControls | dvt.ControlPanel.CONTROLS_CENTER_BUTTON;
    s = jsonObj[dvt.AdfDiagramParser._ATTR_PANDIRECTION];
    if (s) {
      this._diagram.setPanDirection(s);
    }
  }
  else {
    displayedControls = displayedControls & ~dvt.ControlPanel.CONTROLS_CENTER_BUTTON;
  }

  s = jsonObj[dvt.AdfDiagramParser._ATTR_ZOOMING];
  var zoomingEnabled = 'none' != s;
  this._diagram.SetZoomingEnabled(zoomingEnabled);
  if (zoomingEnabled) {
    displayedControls = displayedControls | dvt.ControlPanel.CONTROLS_ZOOM;
    displayedControls = displayedControls | dvt.ControlPanel.CONTROLS_ZOOM_TO_FIT_BUTTON;
  }
  else {
    displayedControls = displayedControls & ~dvt.ControlPanel.CONTROLS_ZOOM;
    displayedControls = displayedControls & ~dvt.ControlPanel.CONTROLS_ZOOM_TO_FIT_BUTTON;
  }

  this._diagram.setDisplayedControls(displayedControls);

  s = jsonObj[dvt.AdfDiagramParser._ATTR_SHOW_RESTORE];
  if (s) {
    var showRestore = (s == 'off' ? 'off' : 'on');
    this._diagram.SetShowRestore(showRestore);
  }

  s = jsonObj[dvt.AdfDiagramParser._ATTR_CENTER_NODE];
  if (s)
    this._diagram.setCenterNode(s);

  s = jsonObj[dvt.AdfDiagramParser._ATTR_ROOT_NODE];
  if (s)
    this._diagram._drillRoot = s; // processed in diagram.stopParsingData

  var panZoom = {};
  var setPanZoom = false;

  s = jsonObj[dvt.AdfDiagramParser._ATTR_PAN_X];
  if (!isNaN(s)) {
    setPanZoom = true;
    panZoom.panX = parseFloat(s);
  }

  s = jsonObj[dvt.AdfDiagramParser._ATTR_PAN_Y];
  if (!isNaN(s)) {
    setPanZoom = true;
    panZoom.panY = parseFloat(s);
  }

  s = jsonObj[dvt.AdfDiagramParser._ATTR_ZOOM];
  if (!isNaN(s)) {
    setPanZoom = true;
    panZoom.zoom = parseFloat(s);
  }

  if (setPanZoom) {
    this._diagram._panZoom = panZoom;
  }

  //drop target styles
  s = jsonObj[dvt.AdfDiagramParser._ATTR_DROP_TARGET_BACKGROUND_COLOR];
  if (s) {
    this._diagram.setDropFeedbackFillColor(s);
  }

  s = jsonObj[dvt.AdfDiagramParser._ATTR_DROP_TARGET_BORDER_COLOR];
  if (s) {
    this._diagram.setDropFeedbackBorderColor(s);
  }

  s = jsonObj[dvt.AdfDiagramParser._ATTR_DROP_TARGET_OPACITY];
  if (s) {
    this._diagram.setDropFeedbackFillAlpha(parseFloat(s));
  }

  // panelCard detailItemIndexes
  s = jsonObj[dvt.AdfDiagramParser._ATTR_CARD_INDEXES];
  if (s) {
    this._diagram.setCardIndexes(s);
  }

  s = jsonObj[dvt.AdfDiagramParser._ATTR_EMPTY_TEXT];
  if (s) {
    this._diagram.setEmptyText(s);
  }

  s = jsonObj['convertDataFlavorFunc'];
  if (s) {
  //From the server produced Json Object @HtmlUpdateOk
  //BUG 34184877 - MAKE DVT COMPATIBLE WITH CSP STANDARDS
  this._diagram.ConvertDataFlavorFunc =(function (value) 
  {  
    var result;  
    var dataFlavorElems = value.split('/');  
    if (dataFlavorElems[0].indexOf('UIComponent') >= 0) {  
      result = AdfDataFlavor.getDataFlavorForClass(AdfUIComponent, dataFlavorElems.length > 1 ? dataFlavorElems[1] : undefined); 
    }
    else if (dataFlavorElems[0].indexOf('RowKeySet') >= 0) { 
      result = AdfDataFlavor.getRowKeyDataFlavor(dataFlavorElems.length > 1 ? dataFlavorElems[1] : undefined); 
    }  
    else if (dataFlavorElems[0].indexOf('String') >= 0) { 
      result = AdfDataFlavor.STRING_FLAVOR;
    }  
    else { 
      result = AdfDataFlavor.ANY_FLAVOR;
    }
    return result;
  });
        
  }

  s = jsonObj[dvt.AdfDiagramParser._ATTR_AF_DROP_FLAVORS];
  if (s) {
    this._diagram.DropFlavors = dvt.AdfDiagramJsonParser.ParseDropFlavors(s, this._diagram.ConvertDataFlavorFunc);
  }

  // panelCard skinning keys
  this.ParsePanelCardJson(jsonObj);
  this.ParsePanelCardHeaderJson(jsonObj);
  this.ParsePanelCardNavButtonJson(jsonObj);
  this.ParsePanelCardTrainButtonJson(jsonObj);
  this.ParsePanelCardNextPrevButtonJson(jsonObj);

  return true;
};


/**
 * Parse additional panel card data from JSON object
 * @param {object} jsonObj  JSON object
 */
dvt.AdfDiagramJsonParser.prototype.ParseAdditionalPanelCardData = function(jsonObj) {
  this._diagram.startParsingPanelCardData();

  //  var nodeData = jsonObj[dvt.AdfDiagramJsonParser._ATTR_NODES];
  //  for(var i=0; i < nodeData.length; i++) {
  //    this._parsePanelCardItemData(nodeData[i]);
  //  }
  //
  var data = jsonObj[dvt.AdfDiagramParser._ELEM_DATA];
  if (data) {
    this._parsePanelCardData(data);
  }

  this._diagram.stopParsingPanelCardData();
};


/**
 * Parse the panel card data
 * @private
 * @param {object} jsonObj  JSON object
 */
dvt.AdfDiagramJsonParser.prototype._parsePanelCardData = function(jsonObj) {
  //  var childNodes = xmlNode.getChildNodes();
  //  for (var i = 0; i < childNodes.length; i++) {
  //    var child = childNodes[i];
  //    if (child && child.getName() == dvt.AdfDiagramParser._ELEM_NODE_DATA) {
  //      this._parsePanelCardItemData(child);
  //    }
  //  }
  // Parse Node Data
  var nodeData = jsonObj[dvt.AdfDiagramParser._ELEM_NODE_DATA];
  if (nodeData) {
    for (var i = 0; i < nodeData.length; i++) {
      this._parsePanelCardItemData(nodeData[i]);
    }
  }
};


/**
 * Parse panel card item data from JSON node object
 * @param {object} nodeData  JSON node data
 */
dvt.AdfDiagramJsonParser.prototype._parsePanelCardItemData = function(nodeData) {
  var s = nodeData[dvt.AdfDiagramParser._ATTR_ID];

  if (s) {
    var attributes = nodeData[dvt.AdfDiagramParser._ELEM_EL];
    if (attributes)
      this._diagram.addPanelCardDataJson(s, attributes);
  }
};


/**
 * Parses the node templates
 * @param {Object} templates  JSON object to parse
 * @param {dvt.AdfDiagramParser} parser  Diagram parser
 * @protected
 */
dvt.AdfDiagramJsonParser.prototype.parseNodeTemplates = function(jsonObj, parser) {
  for (var i = 0; i < jsonObj.length; i++) {
    var nodeDef = parser.CreateNodeDef();
    this.parseNodeAttributes(jsonObj[i], nodeDef);
    this.parseNodeFacets(jsonObj[i], nodeDef);
    parser._diagram.addNodeDef(nodeDef);
  }
};


/**
 * Parses the node attributes
 * @param {Object} jsonObj  JSON object to parse
 * @param {dvt.AdfDiagramNodeDef} nodeDef  Node template created from JSON object
 * @protected
 */
dvt.AdfDiagramJsonParser.prototype.parseNodeAttributes = function(jsonObj, nodeDef) {
  var value;
  for (var key in jsonObj) {
    if (jsonObj.hasOwnProperty(key)) {
      value = jsonObj[key];
      switch (key) {
        case dvt.AdfDiagramParser._ATTR_ID:
          nodeDef.setId(value);
          break;
        case dvt.AdfDiagramParser._ATTR_WIDTH:
          nodeDef.setWidth(value);
          break;
        case dvt.AdfDiagramParser._ATTR_HEIGHT:
          nodeDef.setHeight(value);
          break;
        case dvt.AdfDiagramParser._ATTR_AF_INLINE_STYLE:
          nodeDef.setInlineStyle(value);
          break;
        case dvt.AdfDiagramParser._ATTR_AF_STYLE_CLASS:
          nodeDef.setStyleClass(value);
          break;
        case dvt.AdfDiagramParser._ATTR_AF_READ_ONLY:
          nodeDef.setReadOnly(value);
          break;
        case dvt.AdfDiagramParser._ATTR_AF_DRAGGABLE:
          nodeDef.setDraggable(value);
          break;
        case dvt.AdfDiagramParser._ATTR_AF_DRAG_FLAVOR:
          nodeDef.setDragFlavor(value);
          break;
        case dvt.AdfDiagramParser._ATTR_AF_DROP_FLAVORS:
          nodeDef.setDropFlavors(value);
          break;
        case dvt.AdfDiagramParser._ATTR_SELECTABLE:
          nodeDef.setSelectable(value);
          break;
        case dvt.AdfDiagramParser._ATTR_AF_SHORT_DESC:
          nodeDef.setShortDesc(value);
          break;
        case dvt.AdfDiagramParser._ATTR_LAYOUT:
          nodeDef.setLayout(value);
          break;
        case dvt.AdfDiagramParser._ATTR_LABEL:
          nodeDef.setLabel(value);
          break;
        case dvt.AdfDiagramParser._ATTR_CONTAINER_STYLE:
          nodeDef.setContainerStyle(value);
          break;
        case dvt.AdfDiagramParser._ATTR_OVERVIEW_STYLE:
          nodeDef.setOverviewStyle(value);
          break;
        case dvt.AdfDiagramParser._ATTR_OVERVIEW_CONTAINER_STYLE:
          nodeDef.setOverviewContainerStyle(value);
          break;
        default :
          break;
      }
    }
  }
};


/**
 * Parses the node facets
 * @param {JSON object} jsonObj  JSON object to parse
 * @param {dvt.AdfDiagramNodeDef} nodeDef  Node template created from JSON object
 * @protected
 */
dvt.AdfDiagramJsonParser.prototype.parseNodeFacets = function(jsonObj, nodeDef) {
  var value;
  var template;
  for (var key in jsonObj) {
    if (jsonObj.hasOwnProperty(key)) {
      value = jsonObj[key];
      if (key === 'zoom100' || key === 'zoom75' || key === 'zoom50' || key === 'zoom25') {
        template = dvt.AfComponentFactory.parseJson(value);
        if (template) {
          nodeDef.setNodeTemplate(template, key);
        }
      }
      else if (key === 'label') {
        template = dvt.AfComponentFactory.parseJson(value);
        if (template) {
          nodeDef.setLabelTemplate(template);
        }
      }
      else if (key === 'overlay') {
        template = dvt.AfComponentFactory.parseJson(value);
        if (template) {
          nodeDef.setOverlayTemplate(template);
        }
      }
      else if (key === 'background') {
        template = dvt.AfComponentFactory.parseJson(value);
        if (template) {
          nodeDef.setBackgroundTemplate(template);
        }
      }
      else if (key === 'containerTop') {
        template = dvt.AfComponentFactory.parseJson(value);
        if (template) {
          nodeDef.setContainerTopTemplate(template);
        }
      }
      else if (key === 'containerBottom') {
        template = dvt.AfComponentFactory.parseJson(value);
        if (template) {
          nodeDef.setContainerBottomTemplate(template);
        }
      }
      else if (key === 'containerLeft') {
        template = dvt.AfComponentFactory.parseJson(value);
        if (template) {
          nodeDef.setContainerLeftTemplate(template);
        }
      }
      else if (key === 'containerRight') {
        template = dvt.AfComponentFactory.parseJson(value);
        if (template) {
          nodeDef.setContainerRightTemplate(template);
        }
      }
      else if (key === 'containerStart') {
        template = dvt.AfComponentFactory.parseJson(value);
        if (template) {
          if (dvt.Agent.isRightToLeft(this._diagram.getCtx())) {
            if (!nodeDef.getContainerRightTemplate()) {
              nodeDef.setContainerRightTemplate(template);
            }
          }
          else {
            if (!nodeDef.getContainerLeftTemplate()) {
              nodeDef.setContainerLeftTemplate(template);
            }
          }
        }
      }
      else if (key === 'containerEnd') {
        template = dvt.AfComponentFactory.parseJson(value);
        if (template) {
          if (dvt.Agent.isRightToLeft(this._diagram.getCtx())) {
            if (!nodeDef.getContainerLeftTemplate()) {
              nodeDef.setContainerLeftTemplate(template);
            }
          }
          else {
            if (!nodeDef.getContainerRightTemplate()) {
              nodeDef.setContainerRightTemplate(template);
            }
          }
        }
      }
      else if (key === 'children') {
        for (var j = 0; j < value.length; j++) {
          var child = dvt.AfComponentFactory.parseJsonElement(value[j]);
          var component = value[j]['component'];
          if (component && child) {
            if (component === dvt.AdfDiagramParser._ELEM_SHOW_POPUP_BEHAVIOR) {
              nodeDef.addShowPopupBehavior(child);
            }
            else if (component === dvt.AdfDiagramParser._ELEM_CLIENT_BEHAVIOR) {
              nodeDef.addClientBehavior(child);
            }
          }
        }
      }
    }
  }
};


/**
 * Parses the link templates
 * @param {String} jsonObj  JSON object to parse
 * @param {dvt.AdfDiagramParser} parser  Diagram parser
 * @public
 */
dvt.AdfDiagramJsonParser.prototype.parseLinkTemplates = function(jsonObj, parser) {
  for (var i = 0; i < jsonObj.length; i++) {
    var linkDef = this.parseLinkTemplate(jsonObj[i]);
    parser._diagram.addLinkDef(linkDef);
  }
};


/**
 * Parses the link template
 * @param {String} jsonObj  JSON object to parse
 * @return {dvt.AdfDiagramLinkDef}  Diagram Link Definition object
 * @public
 */
dvt.AdfDiagramJsonParser.prototype.parseLinkTemplate = function(jsonObj) {
  var linkDef = new dvt.AdfDiagramLinkDef();
  this.parseLinkAttributes(jsonObj, linkDef);
  this.parseLinkFacets(jsonObj, linkDef);
  return linkDef;
};


/**
 * Parses the link attributes
 * @param {Object} jsonObj JSON object to parse
 * @param {dvt.AdfDiagramLinkDef} linkDef Link template created from JSON object
 * @public
 */
dvt.AdfDiagramJsonParser.prototype.parseLinkAttributes = function(jsonObj, linkDef) {
  var value;
  for (var key in jsonObj) {
    if (jsonObj.hasOwnProperty(key)) {
      value = jsonObj[key];

      switch (key) {
        case dvt.AdfDiagramParser._ATTR_ID:
          linkDef.setId(value);
          break;
        case dvt.AdfDiagramParser._ATTR_LINK_WIDTH:
          linkDef.setLinkWidth(value);
          break;
        case dvt.AdfDiagramParser._ATTR_LINK_LENGTH:
          linkDef.setLinkLength(value);
          break;
        case dvt.AdfDiagramParser._ATTR_AF_READ_ONLY:
          linkDef.setReadOnly(value);
          break;
        case dvt.AdfDiagramParser._ATTR_SELECTABLE:
          linkDef.setSelectable(value);
          break;
        case dvt.AdfDiagramParser._ATTR_LINK_COLOR:
          if (dvt.AdfDiagramParser.validateColor(value))
            linkDef.setLinkColor(value);
          break;
        case dvt.AdfDiagramParser._ATTR_LINK_TYPE:
          linkDef.setLinkType(value);
          break;
        case dvt.AdfDiagramParser._ATTR_LABEL:
          linkDef.setLabel(value);
          break;
        case dvt.AdfDiagramParser._ATTR_LINK_STYLE:
          linkDef.setLinkStyle(value);
          break;
        case dvt.AdfDiagramParser._ATTR_AF_RENDERED:
          linkDef.setRendered(value);
          break;
        case dvt.AdfDiagramParser._ATTR_AF_SHORT_DESC:
          linkDef.setShortDesc(value);
          break;
        case dvt.AdfDiagramParser._ATTR_END_CONNECTOR_TYPE:
          linkDef.setEndConnectorType(value);
          break;
        case dvt.AdfDiagramParser._ATTR_START_CONNECTOR_TYPE:
          linkDef.setStartConnectorType(value);
          break;
        default :
          break;
      }
    }
  }
  //TODO: Link Children?
};


/**
 * Parses the link facets
 * @param {object} jsonObj JSON object to parse
 * @param {dvt.AdfDiagramLinkDef} linkDef  Node template created from JSON object
 * @public
 */
dvt.AdfDiagramJsonParser.prototype.parseLinkFacets = function(jsonObj, linkDef) {
  var value;
  var template;
  for (var key in jsonObj) {
    if (jsonObj.hasOwnProperty(key)) {
      value = jsonObj[key];
      if (key === 'endConnector' || key === 'startConnector') {
        template = dvt.AfComponentFactory.parseJson(value);
        if (template) {
          linkDef.setConnectorTemplate(template, key);
        }
      }
      else if (key === 'label') {
        template = dvt.AfComponentFactory.parseJson(value);
        if (template) {
          linkDef.setLabelTemplate(template, key);
        }
      }
      else if (key === 'children') {
        for (var j = 0; j < value.length; j++) {
          var child = dvt.AfComponentFactory.parseJsonElement(value[j]);
          if (value[j]['component'] === dvt.AdfDiagramParser._ELEM_SHOW_POPUP_BEHAVIOR && child) {
            linkDef.addShowPopupBehavior(child);
          }
        }
      }
    }
  }
};


/**
 * Parses the node data
 * @param {object} jsonObj  JSON object to parse
 * @param {dvt.AdfDiagramParser} parser  Diagram parser
 * @public
 */
dvt.AdfDiagramJsonParser.prototype.parseNodeData = function(jsonObj, parser) {
  for (var i = 0; i < jsonObj.length; i++) {
    var nodeData = parser.CreateNodeData();
    this.parseNode(jsonObj[i], nodeData, parser);
    parser._diagram.addNodeData(nodeData);
  }
};


/**
 * Parses the node data
 * @param {Object} jsonObj  JSON object to parse
 * @param {dvt.AdfDiagramNodeData} nodeData  Node data object created from JSON object
 * @param {dvt.AdfDiagramParser} parser  Diagram parser
 * @public
 */
dvt.AdfDiagramJsonParser.prototype.parseNode = function(jsonObj, nodeData, parser) {
  var value;
  for (var key in jsonObj) {
    if (jsonObj.hasOwnProperty(key)) {
      value = jsonObj[key];

      switch (key) {
        case dvt.AdfDiagramParser._ATTR_ID:
          nodeData.setId(value);
          break;
        case dvt.AdfDiagramParser._ATTR_GROUP_ID:
          nodeData.setGroupId(value);
          break;
        case dvt.AdfDiagramParser._ATTR_STRUCTURE_ONLY:
          if (value === true) {
            nodeData.setStructureOnly(true);
          }
          break;
        case dvt.AdfDiagramParser._ATTR_TEMPLATE_ID:
          nodeData.setTemplateId(value);
          break;
        case dvt.AdfDiagramParser._ATTR_ROW_INDEX:
          nodeData.setRowIndex(parseInt(value));
          break;
        case dvt.AdfDiagramParser._ATTR_ATTR_GRPS:
          nodeData.setAttributeGroups(value.split(dvt.AdfDiagramParser._SEMICOLON));
          break;
        case dvt.AdfDiagramParser._ATTR_SHOW_ISOLATE:
          nodeData.setShowIsolate(value);
          break;
        case dvt.AdfDiagramParser._ATTR_SHOW_EXPAND_CHILDREN:
          nodeData.setShowExpandChildren(value);
          break;
        case dvt.AdfDiagramParser._ATTR_SHOW_DRILL:
          nodeData.setShowDrill(value);
          break;
        case dvt.AdfDiagramParser._ATTR_SHOW_STACK:
          nodeData.setShowStack(value);
          break;
        case dvt.AdfDiagramParser._ATTR_SHOW_NODE_ACTIONS:
          nodeData.setShowNodeActions(value);
          break;
        case dvt.AdfDiagramParser._ATTR_LAYOUT_ATTRS:
          nodeData.setLayoutAttributes(value);
          break;
        case dvt.AdfDiagramParser._ATTR_CONTAINER:
          nodeData.setContainer(value);
          if (nodeData.isContainer()) {
            value = jsonObj[dvt.AdfDiagramParser._ATTR_CHILDREN_FETCHED];
            if (value) {
              nodeData.setChildrenFetched(value);
            }
          }
          break;
        case dvt.AdfDiagramParser._ELEM_EL:
          nodeData.setElAttributes(value);
          break;
        default :
          break;
      }
    }
  }
};


/**
 * Parses the link data
 * @param {object} jsonObj  JSON object to parse
 * @param {dvt.AdfDiagramParser} parser  Diagram parser
 * @public
 */
dvt.AdfDiagramJsonParser.prototype.parseLinkData = function(jsonObj, parser) {
  for (var i = 0; i < jsonObj.length; i++) {
    var linkData = new DvtAdfDiagramLinkData();
    this.parseLink(jsonObj[i], linkData, parser);
    parser._diagram.addLinkData(linkData);
  }
};


/**
 * Parses the link data
 * @param {object} jsonObj  JSON object to parse
 * @param {DvtAdfDiagramLinkData} linkData  Link data object created from JSON object
 * @param {dvt.AdfDiagramParser} parser  Diagram parser
 * @public
 */
dvt.AdfDiagramJsonParser.prototype.parseLink = function(jsonObj, linkData, parser) {
  var value;
  for (var key in jsonObj) {
    if (jsonObj.hasOwnProperty(key)) {
      value = jsonObj[key];

      switch (key) {
        case dvt.AdfDiagramParser._ATTR_ID:
          linkData.setId(value);
          break;
        case dvt.AdfDiagramParser._ATTR_GROUP_ID:
          linkData.setGroupId(value);
          break;
        case dvt.AdfDiagramParser._ATTR_TEMPLATE_ID:
          linkData.setTemplateId(value);
          break;
        case dvt.AdfDiagramParser._ATTR_PARENT_ID:
          linkData.setStartId(value);
          break;
        case dvt.AdfDiagramParser._ATTR_CHILD_ID:
          linkData.setEndId(value);
          break;
        case dvt.AdfDiagramParser._ATTR_LAYOUT_ATTRS:
          linkData.setLayoutAttributes(value);
          break;
        case dvt.AdfDiagramParser._ATTR_ROW_INDEX:
          linkData.setRowIndex(value);
          break;
        case dvt.AdfDiagramParser._ELEM_EL:
          linkData.setElAttributes(value);
          break;
        default :
          break;
      }
    }
  }
};


/**
 * Parses the cards
 * @param {object} jsonObj  the JSON object
 * @param {dvt.AdfDiagramParser} parser  Dvt diagram parser
 * @public
 */
dvt.AdfDiagramJsonParser.prototype.parseCards = function(jsonObj, parser) {
  var value;
  for (var key in jsonObj) {
    if (jsonObj.hasOwnProperty(key)) {
      value = jsonObj[key];
      if (!value)
        continue;

      switch (key) {
        //defer panelCard loading
        case dvt.AdfDiagramParser._ATTR_NODE_SDI_MAP:
          parser._diagram.setNodeShowDetailItemIndexMap(value);
          break;
        //Bug 14534257 - HTML5: panelCard of expanded child node didn't sync to parent
        case dvt.AdfDiagramParser._ATTR_NEW_NODE_SDI_MAP:
          parser._diagram.setShowDetailItems(value);
          break;
        //Bug fix: 19270204 - diagram panel card nav buttons are not aligned
        //Use the skin name to position the panel card navigation buttons
        case dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_SKIN:
          parser._diagram.setCardNavButtonSkin(value);
          break;
        case dvt.AdfDiagramParser._ELEM_CARD:
          var cardList = new Array();
          for (var i = 0; i < value.length; i++) {
            var cardKey = value[i];
            cardList.push([cardKey[dvt.AdfDiagramParser._ATTR_CARD_TEXT],
              cardKey[dvt.AdfDiagramParser._ATTR_CARD_ICON],
              cardKey[dvt.AdfDiagramParser._ATTR_CARD_ID]]);
          }
          if (cardList.length > 0)
            this._diagram.setCardSelectorList(cardList);
          break;
      }
    }
  }
};


/**
 * Parses the overview window
 * @param {object} jsonObj  the JSON object
 * @param {dvt.AdfDiagramParser} parser  Dvt diagram parser
 */
dvt.AdfDiagramJsonParser.prototype.ParseOverviewWindow = function(jsonObj, parser) {
  if (jsonObj) {
    var width = 108;
    var height = 81;
    var ovWindow = new dvt.OverviewWindow(parser._diagram.getCtx(), 'dgOverviewWindow', 0, 0, width, height);
    ovWindow.setDisclosed(jsonObj['disclosed']);
    var ovStyleMap = ovWindow.getSkinStyle();
    if (!ovStyleMap)
      ovStyleMap = new Object();

    ovStyleMap[dvt.OverviewWindow.VIEWPORT_BORDER_COLOR] = jsonObj[dvt.AdfDiagramParser._ATTR_OV_WIN_VIEWPORT_BORDER_COLOR];
    ovStyleMap[dvt.OverviewWindow.VIEWPORT_BG_COLOR] = jsonObj[dvt.AdfDiagramParser._ATTR_OV_WIN_VIEWPORT_BG_COLOR];
    ovWindow.setSkinStyle(ovStyleMap);

    parser._diagram.setOverviewWindow(ovWindow);
  }
};


/**
 * Parses the overview content
 * @param {object} jsonObj  the JSON object
 */
dvt.AdfDiagramJsonParser.prototype.parseOverviewContentJson = function(jsonObj) {
  var ovStyleMap = this._diagram.getOverviewWindowContentStyle();
  if (!ovStyleMap) {
    ovStyleMap = new Object();
  }
  ovStyleMap[dvt.AdfDiagram.OVERVIEW_STYLE] = new dvt.CSSStyle(jsonObj[dvt.AdfDiagramParser._ATTR_OV_WIN_NODE]);
  ovStyleMap[dvt.AdfDiagram.OVERVIEW_CONTAINER_STYLE] = new dvt.CSSStyle(jsonObj[dvt.AdfDiagramParser._ATTR_OV_WIN_DISCLOSED_NODE]);

  this._diagram.setOverviewWindowContentStyle(ovStyleMap);
};


/**
 * Parses stacking styles from JSON object
 * @param {object} jsonObj  the JSON object
 */
dvt.AdfDiagramJsonParser.prototype.parseStackingStylesJson = function(jsonObj) {
  var s;
  s = jsonObj[dvt.AdfDiagramParser._ATTR_STACK_PREVIEW_COLOR];
  if (s && (s = dvt.AdfDiagramParser.validateColor(s)))
    this._diagram.setStackPreviewColor(s);

  s = jsonObj[dvt.AdfDiagramParser._ATTR_STACK_BADGE_STYLE];
  if (s)
    this._diagram.setStackBadgeStyles(new dvt.CSSStyle(s));

  s = jsonObj[dvt.AdfDiagramParser._ATTR_UNDERLAY_STYLE];
  if (s)
    this._diagram.setStackUnderlayStyles(new dvt.CSSStyle(s));
};


/**
 * Parses stack preview from JSON object
 * @param {object} jsonObj  the JSON object
 */
dvt.AdfDiagramJsonParser.prototype.parseStackPreviewJson = function(jsonObj) {
  var stackPreviewPanZoom = {};
  var setPanZoom = false;

  var s = jsonObj[dvt.AdfDiagramParser._ATTR_PREVIEW_NODE];
  if (s)
    this._diagram._stackPreviewNode = s;

  s = jsonObj[dvt.AdfDiagramParser._ATTR_PREVIEW_PAN_X];
  if (!isNaN(s)) {
    stackPreviewPanZoom.panX = parseFloat(s);
    setPanZoom = true;
  }
  s = jsonObj[dvt.AdfDiagramParser._ATTR_PREVIEW_PAN_Y];
  if (!isNaN(s)) {
    stackPreviewPanZoom.panY = parseFloat(s);
    setPanZoom = true;
  }
  s = jsonObj[dvt.AdfDiagramParser._ATTR_PREVIEW_ZOOM];
  if (!isNaN(s)) {
    stackPreviewPanZoom.zoom = parseFloat(s);
    setPanZoom = true;
  }
  if (setPanZoom) {
    this._diagram._stackPreviewPanZoom = stackPreviewPanZoom;
  }
};


/**
 * Parses styles for Diagram buttons from JSON object
 * @param {object} jsonObj  The JSON object containing button styles
 */
dvt.AdfDiagramJsonParser.prototype.parseDiagramButtonStylesJson = function(jsonObj) {
  var s;

  var cssStyleEna = new dvt.CSSStyle();
  s = jsonObj[dvt.AdfDiagramParser._ATTR_BUTTON_STYLE_ENA];
  if (s)
    cssStyleEna.parseInlineStyle(s);

  var cssStyleOvr;
  s = jsonObj[dvt.AdfDiagramParser._ATTR_BUTTON_STYLE_OVR];
  if (s)
    cssStyleOvr = new dvt.CSSStyle(s);
  else
    cssStyleOvr = cssStyleEna.clone();

  var cssStyleDwn;
  s = jsonObj[dvt.AdfDiagramParser._ATTR_BUTTON_STYLE_DWN];
  if (s)
    cssStyleDwn = new dvt.CSSStyle(s);
  else
    cssStyleDwn = cssStyleEna.clone();

  var buttonStyles = [];
  buttonStyles[dvt.AdfDiagram.BUTTON_STATE_ENA] = cssStyleEna;
  buttonStyles[dvt.AdfDiagram.BUTTON_STATE_OVR] = cssStyleOvr;
  buttonStyles[dvt.AdfDiagram.BUTTON_STATE_DWN] = cssStyleDwn;
  this._diagram.setDiagramButtonStyles(buttonStyles);
};


/**
 * Parses diagram isolation from JSON object
 * @param {object} jsonObj  The JSON object
 */
dvt.AdfDiagramJsonParser.prototype.ParseDiagramIsolation = function(jsonObj) {
  var s;

  s = jsonObj[dvt.AdfDiagramParser._ATTR_ISOLATED_NODES];
  if (s) {
    this._diagram._isolatedNodes = s.split(',');
    this._diagram._isolatedNodeMap = {};
    for (var i = 0; i < this._diagram._isolatedNodes.length; i++) {
      this._diagram._isolatedNodeMap[this._diagram._isolatedNodes[i]] = false;
    }
  }
};


/**
 * Parses selection from JSON object
 * @param {object} jsonObj  The JSON object
 */
dvt.AdfDiagramJsonParser.prototype.parseSelectionJson = function(jsonObj) {
  var s;

  //Parse Selection Mode
  s = jsonObj[dvt.AdfDiagramParser._ATTR_SELECTION_MODE];
  this._diagram.setSelectionMode(s); // Set even when null to enable "sticky" link highlighting

  //Parse Selected Nodes
  s = jsonObj[dvt.AdfDiagramParser._ATTR_SELECTED_NODES];
  if (s)
    this._diagram._selectedNodes = s.split(',');

  //Parse Selected Links
  s = jsonObj[dvt.AdfDiagramParser._ATTR_SELECTED_LINKS];
  if (s)
    this._diagram._selectedLinks = s.split(',');
};


/**
 * Parses breadcrumbs from JSON object
 * @param {object} jsonObj  The JSON object
 */
dvt.AdfDiagramJsonParser.prototype.parseBreadcrumbsJson = function(jsonObj) {
  var breadcrumbsStyle = dvt.AdfDiagramJsonParser.getBreadcrumbsStyles(jsonObj, dvt.AdfDiagramParser._ATTR_BREADCRUMBS_STYLE,
      dvt.AdfDiagramParser._ATTR_BREADCRUMBS_HOVER_STYLE,
      dvt.AdfDiagramParser._ATTR_BREADCRUMBS_ACTIVE_STYLE,
      dvt.AdfDiagramParser._ATTR_BREADCRUMBS_DISABLED_STYLE);
  if (breadcrumbsStyle)
    this._diagram.setBreadcrumbsStyles(breadcrumbsStyle);
};


/**
 * Parses styles for Diagram breadbrumb buttons
 * @param {object} jsonObj  The JSON object containing breadcrumb styles
 * @param {string} ena An attribute for enabled style
 * @param {string} hov An attribute for hovered style
 * @param {string} act An attribute for active style
 * @param {string} dis An attribute for disabled style
 */
dvt.AdfDiagramJsonParser.getBreadcrumbsStyles = function(jsonObj, ena, hov, act, dis) {
  // parse skin
  var enaSkinStyles = jsonObj[ena];

  var hovSkinStyles = jsonObj[hov] ? jsonObj[hov] : enaSkinStyles;
  var actSkinStyles = jsonObj[act] ? jsonObj[act] : enaSkinStyles;
  var disSkinStyles = jsonObj[dis] ? jsonObj[dis] : enaSkinStyles;

  // since the state can contain partial styles, use enabled style as a base
  hovSkinStyles = dvt.AdfDiagramParser.mergeStyles(enaSkinStyles, hovSkinStyles);
  actSkinStyles = dvt.AdfDiagramParser.mergeStyles(enaSkinStyles, actSkinStyles);
  disSkinStyles = dvt.AdfDiagramParser.mergeStyles(enaSkinStyles, disSkinStyles);

  var styles = [];
  styles[dvt.AdfDiagram.BUTTON_STATE_ENA] = enaSkinStyles;
  styles[dvt.AdfDiagram.BUTTON_STATE_OVR] = hovSkinStyles;
  styles[dvt.AdfDiagram.BUTTON_STATE_DWN] = actSkinStyles;
  styles[dvt.AdfDiagram.BUTTON_STATE_DIS] = disSkinStyles;

  return styles;
};


/**
 * Parses palette related attributes
 * @param {object} jsonObj  The JSON object to parse
 */
dvt.AdfDiagramJsonParser.prototype.parsePaletteJson = function(jsonObj) {
  var s;

  var cssStyleEna = new dvt.CSSStyle();
  s = jsonObj[dvt.AdfDiagramParser._ATTR_PALETTE_SECTION_STYLE];
  if (s)
    cssStyleEna.parseInlineStyle(s);

  var cssStyleOvr;
  s = jsonObj[dvt.AdfDiagramParser._ATTR_PALETTE_SECTION_HOVER_STYLE];
  if (s)
    cssStyleOvr = new dvt.CSSStyle(s);
  else
    cssStyleOvr = cssStyleEna.clone();

  var cssStyleDwn;
  s = jsonObj[dvt.AdfDiagramParser._ATTR_PALETTE_SECTION_ACTIVE_STYLE];
  if (s)
    cssStyleDwn = new dvt.CSSStyle(s);
  else
    cssStyleDwn = cssStyleEna.clone();

  var paletteStyleMap = new Object();
  paletteStyleMap['skin'] = this._diagram.getSkinName();
  paletteStyleMap['sectionHeader'] = new Object();
  paletteStyleMap['sectionHeader']['styleEna'] = cssStyleEna;
  paletteStyleMap['sectionHeader']['styleOvr'] = cssStyleEna;
  paletteStyleMap['sectionHeader']['styleDwn'] = cssStyleEna;


  paletteStyleMap['sectionItem'] = new Object();
  s = jsonObj[dvt.AdfDiagramParser._ATTR_PALETTE_ITEM_STYLE];
  if (s) {
    var cssItemStyle = new dvt.CSSStyle();
    cssItemStyle.parseInlineStyle(s);
    paletteStyleMap['sectionItem']['style'] = cssItemStyle;
  }

  s = jsonObj['paletteItemHoverStyle'];
  if (s) {
    paletteStyleMap['sectionItem']['fillColorHover'] = s;
  }

  s = jsonObj['paletteItemSelectedStyle'];
  if (s) {
    paletteStyleMap['sectionItem']['fillColorSelected'] = s;
  }

  this._diagram.setPaletteStyles(paletteStyleMap);
};


/**
 * Parses attributes related to diagram node styles
 * @param {object} jsonObj  The JSON object to parse
 * @protected
 */
dvt.AdfDiagramJsonParser.prototype.ParseNodeStylesJson = function(jsonObj) {
  var nodeStyles = {};

  var setNodeStyle = false;

  var s = jsonObj[dvt.AdfDiagramParser._ATTR_NODE_SELECT_COLOR];
  if (s && (s = dvt.AdfDiagramParser.validateColor(s))) {
    setNodeStyle = true;
    nodeStyles[dvt.AdfDiagramNode.SELECTED_COLOR] = s;
  }

  s = jsonObj[dvt.AdfDiagramParser._ATTR_NODE_HOVER_INNER_COLOR];
  if (s && (s = dvt.AdfDiagramParser.validateColor(s))) {
    setNodeStyle = true;
    nodeStyles[dvt.AdfDiagramNode.HOVER_INNER_COLOR] = s;
  }

  s = jsonObj[dvt.AdfDiagramParser._ATTR_NODE_HOVER_OUTER_COLOR];
  if (s && (s = dvt.AdfDiagramParser.validateColor(s))) {
    setNodeStyle = true;
    nodeStyles[dvt.AdfDiagramNode.HOVER_OUTER_COLOR] = s;
  }

  s = jsonObj[dvt.AdfDiagramParser._ATTR_NODE_CONTAINER_STYLE];
  if (s) {
    setNodeStyle = true;
    nodeStyles[dvt.AdfDiagramNode.CONTAINER_STYLE] = new dvt.CSSStyle(s);
  }

  s = jsonObj[dvt.AdfDiagramParser._ATTR_NODE_LABEL_STYLE];
  if (s) {
    setNodeStyle = true;
    nodeStyles[dvt.AdfDiagramNode.LABEL_STYLE] = new dvt.CSSStyle(s);
  }

  if (setNodeStyle) {
    this._diagram.setNodeStyles(nodeStyles);
  }
};


/**
 * Parses attributes related to diagram link styles
 * @param {objet} jsonObj  The JSON object
 * @protected
 */
dvt.AdfDiagramJsonParser.prototype.ParseLinkStylesJson = function(jsonObj) {
  var linkStyles = {};

  var setLinkStyle = false;

  var s = jsonObj[dvt.AdfDiagramParser._ATTR_LINK_COLOR];
  if (s && (s = dvt.AdfDiagramParser.validateColor(s))) {
    setLinkStyle = true;
    linkStyles[dvt.AdfDiagramLink.COLOR] = s;
  }

  s = jsonObj[dvt.AdfDiagramParser._ATTR_LINK_SELECT_COLOR];
  if (s && (s = dvt.AdfDiagramParser.validateColor(s))) {
    setLinkStyle = true;
    linkStyles[dvt.AdfDiagramLink.SELECTED_COLOR] = s;
  }

  s = jsonObj[dvt.AdfDiagramParser._ATTR_LINK_HOVER_INNER_COLOR];
  if (s && (s = dvt.AdfDiagramParser.validateColor(s))) {
    setLinkStyle = true;
    linkStyles[dvt.AdfDiagramLink.HOVER_INNER_COLOR] = s;
  }

  s = jsonObj[dvt.AdfDiagramParser._ATTR_LINK_HOVER_OUTER_COLOR];
  if (s && (s = dvt.AdfDiagramParser.validateColor(s))) {
    setLinkStyle = true;
    linkStyles[dvt.AdfDiagramLink.HOVER_OUTER_COLOR] = s;
  }

  s = jsonObj[dvt.AdfDiagramParser._ATTR_LINK_LABEL_STYLE];
  if (s) {
    setLinkStyle = true;
    linkStyles[dvt.AdfDiagramLink.LABEL_STYLE] = new dvt.CSSStyle(s);
  }

  s = jsonObj[dvt.AdfDiagramParser._ATTR_LINK_PROMOTED_COLOR];
  if (s && (s = dvt.AdfDiagramParser.validateColor(s))) {
    setLinkStyle = true;
    linkStyles[dvt.AdfDiagramLink.PROMOTED_COLOR] = s;
  }

  s = jsonObj[dvt.AdfDiagramParser._ATTR_LINK_PROMOTED_STYLE];
  if (s) {
    setLinkStyle = true;
    s = dvt.AdfDiagramParser._LINK_STYLE_CONVERTER[s];
    linkStyles[dvt.AdfDiagramLink.PROMOTED_STYLE] = s ? s : dvt.AdfDiagramLinkDef.LINK_STYLE_DOT;
  }

  s = jsonObj[dvt.AdfDiagramParser._ATTR_LINK_PROMOTED_WIDTH];
  if (s && !isNaN(parseInt(s))) {
    setLinkStyle = true;
    linkStyles[dvt.AdfDiagramLink.PROMOTED_WIDTH] = s;
  }

  if (setLinkStyle) {
    this._diagram.setLinkStyles(linkStyles);
  }
};


/**
 * Parse JSON attributes related to button style
 * @proected
 * @param {object} jsonObj The JSON object to parse
 */
dvt.AdfDiagramJsonParser.prototype.ParseButtonStyleJson = function(jsonObj) {
  var buttonStyleList = [];
  var labelStyleList = [];

  //parse button container attributes
  this.ParseButtonContainerStyle(jsonObj, buttonStyleList, dvt.AdfDiagramParser._ATTR_ENABLED_STATE);
  this.ParseButtonContainerStyle(jsonObj, buttonStyleList, dvt.AdfDiagramParser._ATTR_HOVER_STATE);
  this.ParseButtonContainerStyle(jsonObj, buttonStyleList, dvt.AdfDiagramParser._ATTR_ACTIVE_STATE);
  this._diagram.setButtonStyle(buttonStyleList);

  //parse button label attributes
  this.ParseButtonLabelStyle(jsonObj, labelStyleList, dvt.AdfDiagramParser._ATTR_ENABLED_STATE);
  this.ParseButtonLabelStyle(jsonObj, labelStyleList, dvt.AdfDiagramParser._ATTR_HOVER_STATE);
  this.ParseButtonLabelStyle(jsonObj, labelStyleList, dvt.AdfDiagramParser._ATTR_ACTIVE_STATE);
  this._diagram.setLabelStyle(labelStyleList);
};


/**
 * Parse JSON attributes related to button container style
 * @protected
 * @param {object} jsonObj The JSON object to parse
 * @param {Array} buttonStyleList  Array of button styles for each state
 * @param {string} state  button style for the state
 */
dvt.AdfDiagramJsonParser.prototype.ParseButtonContainerStyle = function(jsonObj, buttonStyleList, state) {
  var s;
  var buttonStyle = new dvt.CSSStyle();
  var bButtonStyle = false;

  //button container
  s = jsonObj[dvt.AdfDiagramParser._ATTR_BUTTON_CONTAINER + state + dvt.AdfDiagramParser._ATTR_BUTTON_FILL_TYPE];
  if (s) {
    bButtonStyle = this._diagramParser.AddButtonStyle(buttonStyle, s, dvt.CSSStyle.FILL_TYPE, bButtonStyle);
  }
  s = jsonObj[dvt.AdfDiagramParser._ATTR_BUTTON_CONTAINER + state + dvt.AdfDiagramParser._ATTR_BUTTON_BACKGROUND_COLOR];
  if (s) {
    bButtonStyle = this._diagramParser.AddButtonStyle(buttonStyle, s, dvt.CSSStyle.BACKGROUND_COLOR, bButtonStyle);
  }
  s = jsonObj[dvt.AdfDiagramParser._ATTR_BUTTON_CONTAINER + state + dvt.AdfDiagramParser._ATTR_BUTTON_BACKGROUND];
  if (s) {
    bButtonStyle = this._diagramParser.AddButtonStyle(buttonStyle, s, dvt.CSSStyle.BACKGROUND, bButtonStyle);
  }
  s = jsonObj[dvt.AdfDiagramParser._ATTR_BUTTON_CONTAINER + state + dvt.AdfDiagramParser._ATTR_BUTTON_BORDER_COLOR];
  if (s) {
    bButtonStyle = this._diagramParser.AddButtonStyle(buttonStyle, s, dvt.CSSStyle.BORDER_COLOR, bButtonStyle);
  }

  if (bButtonStyle)
    buttonStyleList.push(buttonStyle);
  else
    buttonStyleList.push(null);
};


/**
 * Parse JSON attributes related to button label style
 * @protected
 * @param {object} jsonObj The JSON object to parse
 * @param {Array} labelStyleList  Array of button label style
 * @param {string} state  label style for button state
 */
dvt.AdfDiagramJsonParser.prototype.ParseButtonLabelStyle = function(jsonObj, labelStyleList, state) {
  var s = jsonObj[dvt.AdfDiagramParser._ATTR_BUTTON_LABEL + state];
  if (s) {
    var labelStyle = new dvt.CSSStyle();
    labelStyle.parseInlineStyle(s);
    labelStyleList.push(labelStyle);
  } else {
    labelStyleList.push(null);
  }
};


/*-------------------------------------------------------------------------*/
/*   PanelCard                                                             */
/*-------------------------------------------------------------------------*/
/**
 * Parse panel card from JSON object
 * @param {object} jsonObj  JSON object to parse
 * @protected
 */
dvt.AdfDiagramJsonParser.prototype.ParsePanelCardJson = function(jsonObj) {
  var s;

  // general card style
  var cardCSSStyle = new dvt.CSSStyle();
  var bCardStyle = false;
  s = jsonObj[dvt.AdfDiagramParser._ATTR_CARD_FILL_TYPE];
  if (s) {
    cardCSSStyle.setStyle(dvt.CSSStyle.FILL_TYPE, s);
    bCardStyle = true;
  }
  s = jsonObj[dvt.AdfDiagramParser._ATTR_CARD_BORDER_TYPE];
  if (s) {
    cardCSSStyle.setStyle(dvt.CSSStyle.BORDER_TYPE, s);
    bCardStyle = true;
  }
  s = jsonObj[dvt.AdfDiagramParser._ATTR_CARD_BORDER_COLOR];
  if (s) {
    cardCSSStyle.setStyle(dvt.CSSStyle.BORDER_COLOR, s);
    bCardStyle = true;
  }
  s = jsonObj[dvt.AdfDiagramParser._ATTR_CARD_BACKGROUND_COLOR];
  if (s) {
    cardCSSStyle.setStyle(dvt.CSSStyle.BACKGROUND_COLOR, s);
    bCardStyle = true;
  }
  s = jsonObj[dvt.AdfDiagramParser._ATTR_CARD_BORDER_RADIUS];
  if (s) {
    cardCSSStyle.setStyle('border-radius', s);
    bCardStyle = true;
  }
  if (bCardStyle)
    this._diagram.setCardStyle(cardCSSStyle);
};


/**
 * Parse panel card header from JSON object
 * @param {object} jsonObj  JSON object to parse
 * @protected
 */
dvt.AdfDiagramJsonParser.prototype.ParsePanelCardHeaderJson = function(jsonObj) {
  var s;
  // header styles
  var headerStyles = this._diagram.getCardHeaderStyle();
  s = jsonObj[dvt.AdfDiagramParser._ATTR_CARD_HEADER_TEXT];
  this._diagramParser.AddCardHeaderStyle(headerStyles, s, 'zoom100');

  s = jsonObj[dvt.AdfDiagramParser._ATTR_CARD_HEADER_TEXT75];
  this._diagramParser.AddCardHeaderStyle(headerStyles, s, 'zoom75');

  s = jsonObj[dvt.AdfDiagramParser._ATTR_CARD_HEADER_TEXT50];
  this._diagramParser.AddCardHeaderStyle(headerStyles, s, 'zoom50');

  s = jsonObj[dvt.AdfDiagramParser._ATTR_CARD_HEADER_TEXT25];
  this._diagramParser.AddCardHeaderStyle(headerStyles, s, 'zoom25');

  this._diagram.setCardHeaderStyle(headerStyles);
};


/**
 * Prase panel card navigation buttons from JSON object
 * @param {object} jsonObj  JSON object to parse
 * @protected
 */
dvt.AdfDiagramJsonParser.prototype.ParsePanelCardNavButtonJson = function(jsonObj) {
  var s;
  //button styles
  var cardNavButtonStyle = new dvt.CSSStyle();
  var bcardNavButtonStyle = false;

  s = jsonObj[dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_FILL_TYPE];
  bcardNavButtonStyle = this._diagramParser.AddButtonStyle(cardNavButtonStyle, s, dvt.CSSStyle.FILL_TYPE, bcardNavButtonStyle);

  s = jsonObj[dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_BORDER_COLOR];
  bcardNavButtonStyle = this._diagramParser.AddButtonStyle(cardNavButtonStyle, s, dvt.CSSStyle.BORDER_COLOR, bcardNavButtonStyle);

  s = jsonObj[dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_ICON_COLOR];
  bcardNavButtonStyle = this._diagramParser.AddButtonStyle(cardNavButtonStyle, s, dvt.CSSStyle.COLOR, bcardNavButtonStyle);

  s = jsonObj[dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_BACKGROUND_COLOR];
  bcardNavButtonStyle = this._diagramParser.AddButtonStyle(cardNavButtonStyle, s, dvt.CSSStyle.BACKGROUND_COLOR, bcardNavButtonStyle);

  if (bcardNavButtonStyle) {
    if (this._diagram.getSkinName() == dvt.CSSStyle.SKIN_SKYROS)
      cardNavButtonStyle.setStyle(dvt.CSSStyle.FILL_TYPE, 'solid');
    this._diagram.setCardNavButtonStyle(cardNavButtonStyle);
  }

  cardNavButtonStyle = new dvt.CSSStyle();
  bcardNavButtonStyle = false;

  s = jsonObj[dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_HOVER_FILL_TYPE];
  bcardNavButtonStyle = this._diagramParser.AddButtonStyle(cardNavButtonStyle, s, dvt.CSSStyle.FILL_TYPE, bcardNavButtonStyle);

  s = jsonObj[dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_HOVER_BORDER_COLOR];
  bcardNavButtonStyle = this._diagramParser.AddButtonStyle(cardNavButtonStyle, s, dvt.CSSStyle.BORDER_COLOR, bcardNavButtonStyle);

  s = jsonObj[dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_HOVER_ICON_COLOR];
  bcardNavButtonStyle = this._diagramParser.AddButtonStyle(cardNavButtonStyle, s, dvt.CSSStyle.COLOR, bcardNavButtonStyle);

  s = jsonObj[dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_HOVER_BACKGROUND_COLOR];
  bcardNavButtonStyle = this._diagramParser.AddButtonStyle(cardNavButtonStyle, s, dvt.CSSStyle.BACKGROUND_COLOR, bcardNavButtonStyle);

  if (bcardNavButtonStyle) {
    if (this._diagram.getSkinName() == dvt.CSSStyle.SKIN_SKYROS)
      cardNavButtonStyle.setStyle(dvt.CSSStyle.FILL_TYPE, 'solid');
    this._diagram.setCardNavButtonHoverStyle(cardNavButtonStyle);
  }

  cardNavButtonStyle = new dvt.CSSStyle();
  bcardNavButtonStyle = false;

  s = jsonObj[dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_ACTIVE_FILL_TYPE];
  bcardNavButtonStyle = this._diagramParser.AddButtonStyle(cardNavButtonStyle, s, dvt.CSSStyle.FILL_TYPE, bcardNavButtonStyle);

  s = jsonObj[dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_ACTIVE_ICON_COLOR];
  bcardNavButtonStyle = this._diagramParser.AddButtonStyle(cardNavButtonStyle, s, dvt.CSSStyle.COLOR, bcardNavButtonStyle);

  s = jsonObj[dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_ACTIVE_BORDER_COLOR];
  bcardNavButtonStyle = this._diagramParser.AddButtonStyle(cardNavButtonStyle, s, dvt.CSSStyle.BORDER_COLOR, bcardNavButtonStyle);

  s = jsonObj[dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_ACTIVE_BACKGROUND_COLOR];
  bcardNavButtonStyle = this._diagramParser.AddButtonStyle(cardNavButtonStyle, s, dvt.CSSStyle.BACKGROUND_COLOR, bcardNavButtonStyle);

  if (bcardNavButtonStyle) {
    if (this._diagram.getSkinName() == dvt.CSSStyle.SKIN_SKYROS)
      cardNavButtonStyle.setStyle(dvt.CSSStyle.FILL_TYPE, 'solid');
    this._diagram.setCardNavButtonActiveStyle(cardNavButtonStyle);
  }

  cardNavButtonStyle = new dvt.CSSStyle();
  bcardNavButtonStyle = false;

  s = jsonObj[dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_DISABLED_FILL_TYPE];
  bcardNavButtonStyle = this._diagramParser.AddButtonStyle(cardNavButtonStyle, s, dvt.CSSStyle.FILL_TYPE, bcardNavButtonStyle);

  s = jsonObj[dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_DISABLED_BORDER_COLOR];
  bcardNavButtonStyle = this._diagramParser.AddButtonStyle(cardNavButtonStyle, s, dvt.CSSStyle.BORDER_COLOR, bcardNavButtonStyle);

  s = jsonObj[dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_DISABLED_ICON_COLOR];
  bcardNavButtonStyle = this._diagramParser.AddButtonStyle(cardNavButtonStyle, s, dvt.CSSStyle.COLOR, bcardNavButtonStyle);

  s = jsonObj[dvt.AdfDiagramParser._ATTR_CARD_NAV_BUTTON_DISABLED_BACKGROUND_COLOR];
  bcardNavButtonStyle = this._diagramParser.AddButtonStyle(cardNavButtonStyle, s, dvt.CSSStyle.BACKGROUND_COLOR, bcardNavButtonStyle);

  if (bcardNavButtonStyle) {
    if (this._diagram.getSkinName() == dvt.CSSStyle.SKIN_SKYROS)
      cardNavButtonStyle.setStyle(dvt.CSSStyle.FILL_TYPE, 'solid');
    this._diagram.setCardNavButtonDisabledStyle(cardNavButtonStyle);
  }
};


/**
 * Parse panel card train button from JSON object
 * @param {object} jsonObj  JSON object to parse
 */
dvt.AdfDiagramJsonParser.prototype.ParsePanelCardTrainButtonJson = function(jsonObj) {
  var s;

  s = jsonObj[dvt.AdfDiagramParser._ATTR_CARD_TRAIN_BUTTON_VISIBILITY];
  if (s && s.toLowerCase() === 'false')
    this._diagram.setCardTrainButtonVisible(false);

  var bcardTrainButtonStyle;
  var bcardNavButtonStyle;

  //alta train icons
  var cardTrainButtonStyle = new dvt.CSSStyle();
  var enaUrl = jsonObj[dvt.AdfDiagramParser._ATTR_CARD_TRAIN_BUTTON_ICON];
  if (enaUrl) {
    bcardNavButtonStyle = true;
    cardTrainButtonStyle.setIconUrl(enaUrl);
  }
  else {
    bcardTrainButtonStyle = false;

    s = jsonObj[dvt.AdfDiagramParser._ATTR_CARD_TRAIN_BUTTON_BORDER_COLOR];
    bcardNavButtonStyle = this._diagramParser.AddButtonStyle(cardTrainButtonStyle, s, dvt.CSSStyle.BORDER_COLOR, bcardTrainButtonStyle);

    s = jsonObj[dvt.AdfDiagramParser._ATTR_CARD_TRAIN_BUTTON_BACKGROUND_COLOR];
    bcardNavButtonStyle = this._diagramParser.AddButtonStyle(cardTrainButtonStyle, s, dvt.CSSStyle.BACKGROUND_COLOR, bcardTrainButtonStyle);

  }
  if (bcardNavButtonStyle) {
    this._diagram.setCardTrainButtonStyle(cardTrainButtonStyle);
  }

  //alta train hover icons
  cardTrainButtonStyle = new dvt.CSSStyle();
  var hoverUrl = jsonObj[dvt.AdfDiagramParser._ATTR_CARD_TRAIN_BUTTON_HOVER_ICON];
  if (! hoverUrl)
    hoverUrl = enaUrl;
  if (hoverUrl) {
    cardTrainButtonStyle.setIconUrl(hoverUrl);
    this._diagram.setCardTrainButtonHoverStyle(cardTrainButtonStyle);
  }

  //alta train active icons
  cardTrainButtonStyle = new dvt.CSSStyle();
  var activeUrl = jsonObj[dvt.AdfDiagramParser._ATTR_CARD_TRAIN_BUTTON_ACTIVE_ICON];
  if (! activeUrl)
    activeUrl = enaUrl;
  if (activeUrl) {
    bcardNavButtonStyle = true;
    cardTrainButtonStyle.setIconUrl(activeUrl);
  }
  else {
    bcardTrainButtonStyle = false;

    s = jsonObj[dvt.AdfDiagramParser._ATTR_CARD_TRAIN_BUTTON_ACTIVE_BORDER_COLOR];
    bcardNavButtonStyle = this._diagramParser.AddButtonStyle(cardTrainButtonStyle, s, dvt.CSSStyle.BORDER_COLOR, bcardTrainButtonStyle);

    s = jsonObj[dvt.AdfDiagramParser._ATTR_CARD_TRAIN_BUTTON_ACTIVE_BACKGROUND_COLOR];
    bcardNavButtonStyle = this._diagramParser.AddButtonStyle(cardTrainButtonStyle, s, dvt.CSSStyle.BACKGROUND_COLOR, bcardTrainButtonStyle);

  }
  if (bcardNavButtonStyle) {
    this._diagram.setCardTrainButtonActiveStyle(cardTrainButtonStyle);
  }

};


/**
 * Parse panel card next/previous button from JSON object
 * @param {object} jsonObj  JSON object to parse
 * @protected
 */
dvt.AdfDiagramJsonParser.prototype.ParsePanelCardNextPrevButtonJson = function(jsonObj) {
  var buttonImages = dvt.AdfDiagramJsonParser.getButtonImages(jsonObj, dvt.AdfDiagramParser._ATTR_CARD_PREV_ICON,
      dvt.AdfDiagramParser._ATTR_CARD_PREV_HOVER_ICON, dvt.AdfDiagramParser._ATTR_CARD_PREV_ACTIVE_ICON);
  if (buttonImages)
    this._diagram.setCardPrevButtonIcon(buttonImages);

  buttonImages = dvt.AdfDiagramJsonParser.getButtonImages(jsonObj, dvt.AdfDiagramParser._ATTR_CARD_NEXT_ICON,
      dvt.AdfDiagramParser._ATTR_CARD_NEXT_HOVER_ICON, dvt.AdfDiagramParser._ATTR_CARD_NEXT_ACTIVE_ICON);
  if (buttonImages)
    this._diagram.setCardNextButtonIcon(buttonImages);
};


/**
 * Get button images from JSON object
 * @param {object} jsonObj  JSON object
 * @param {String} ena  Enabled button image style
 * @param {String} hov  Hover button image style
 * @param {String} act  Active button image style
 */
dvt.AdfDiagramJsonParser.getButtonImages = function(jsonObj, ena, hov, act) {
  var imageUrls = [];
  var defImgUrl = jsonObj[ena];
  var hovImgUrl = jsonObj[hov] ? jsonObj[hov] : defImgUrl;
  var actImgUrl = jsonObj[act] ? jsonObj[act] : defImgUrl;

  if (!defImgUrl && !hovImgUrl && !actImgUrl)
    return null;
  else {
    imageUrls.push(defImgUrl);
    imageUrls.push(hovImgUrl);
    imageUrls.push(actImgUrl);
  }
  return imageUrls;
};

/**
 * Parses scrollbar styles used by the search results panel
 * @param {object} jsonObj The JSON object to parse
 */
dvt.AdfDiagramJsonParser.prototype.parseScrollbarStylesJson = function(jsonObj) {
  var sbStyleMap = new Object();
  // scrollbar styles for scrollable container
  dvt.StyleUtils.setStyle(sbStyleMap, DvtScrollbar.BACKGROUND, jsonObj[dvt.AdfDiagramParser._ATTR_SCROLLBAR_BACKGROUND]);
  dvt.StyleUtils.setStyle(sbStyleMap, DvtScrollbar.BORDER_COLOR, jsonObj[dvt.AdfDiagramParser._ATTR_SCROLLBAR_BORDER_COLOR]);
  dvt.StyleUtils.setStyle(sbStyleMap, DvtScrollbar.HANDLE_COLOR, jsonObj[dvt.AdfDiagramParser._ATTR_SCROLLBAR_HANDLE_COLOR]);
  dvt.StyleUtils.setStyle(sbStyleMap, DvtScrollbar.HANDLE_ACTIVE_COLOR, jsonObj[dvt.AdfDiagramParser._ATTR_SCROLLBAR_HANDLE_ACTIVE_COLOR]);
  dvt.StyleUtils.setStyle(sbStyleMap, DvtScrollbar.HANDLE_HOVER_COLOR, jsonObj[dvt.AdfDiagramParser._ATTR_SCROLLBAR_HANDLE_HOVER_COLOR]);
  var styles = dvt.JsonUtils.merge(sbStyleMap, this._diagram.getSubcomponentStyles());
  this._diagram.setSubcomponentStyles(styles);
};

/**
 * Parses space separated data flavors for the diagram or diagram node
 * @param {string} value space separated data flavors for the diagram or diagram node
 * @param {function} convertDataFlavorFunc function that converts string into data flavor
 * @return {array} array of AdfDataFlavor objects
 */
dvt.AdfDiagramJsonParser.ParseDropFlavors = function(value, convertDataFlavorFunc) {
  if (!(value && convertDataFlavorFunc))
    return null;
  var dropFlavors = [];
  var arrFlavors = value.split(' ');
  for (var i = 0; i < arrFlavors.length; i++) {
    dropFlavors.push(convertDataFlavorFunc(arrFlavors[i]));
  }
  return dropFlavors;
};
// Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
/**
 * @constructor
 */
dvt.AdfDiagramEventManager = function(context, callback, callbackObj) {
  this.Init(context, callback, callbackObj);
};

dvt.Obj.createSubclass(dvt.AdfDiagramEventManager, dvt.EventManager, 'dvt.AdfDiagramEventManager');

dvt.AdfDiagramEventManager.SWIPE_KEY = 'swipeKey';

dvt.AdfDiagramEventManager.prototype.Init = function(context, callback, callbackObj) {
  this._diagram = callbackObj;
  dvt.AdfDiagramEventManager.superclass.Init.call(this, context, callback, callbackObj);

  this._swipeStartPos = null;
  this._showKeyboardFocusByDefault = false;

  //save current mouse info
  this._currPageX = 0;
  this._currPageY = 0;
  this._currTarget = null;
  this._touchId = null;
};

dvt.AdfDiagramEventManager.prototype.IsPropagationEnabled = function(eventType, event, displayable) {

  if (event && event.isPropagationStopped && event.isPropagationStopped())
    return false;

  var logicalObject = this.GetLogicalObject(displayable);
  return !(this.ConsumesClick(logicalObject));
};


/**
 * Keyboard event handler. Handles keyboard navigation and triggering of context menus
 * @param {dvt.KeyboardEvent} event
 * @return {boolean} true if this event manager has consumed the event
 */
dvt.AdfDiagramEventManager.prototype.ProcessKeyboardEvent = function(event)
{
  var eventConsumed = true;
  var keyCode = event.keyCode;
  var navigable = this.getFocus();
  var coords;
  var pageCoords;

  if (keyCode == dvt.KeyboardEvent.STAR || keyCode == dvt.KeyboardEvent.NUMPAD_STAR)
  {
    var overviewPanel = this._diagram.__getOverviewWindowPanel();
    if (overviewPanel) {
      if (overviewPanel instanceof dvt.CollapsiblePanel) {
        overviewPanel.setCollapsed(!overviewPanel.isCollapsed());
      }
      else if (overviewPanel instanceof dvt.PanelDrawer) {
        overviewPanel.setDisclosed(!overviewPanel.isDisclosed());
      }
    }
    dvt.EventManager.consumeEvent(event);
  }
  else if (keyCode == dvt.KeyboardEvent.GRAVE_ACCENT)
  {
    var panelDrawer = this._diagram.__getPanelDrawer();
    if (panelDrawer) {
      panelDrawer.setDisclosed(!panelDrawer.isDisclosed());
      this._diagram.setContextKeyboardHandlers();
    }
    dvt.EventManager.consumeEvent(event);
  }
  else if ((keyCode == dvt.KeyboardEvent.ZERO || keyCode == dvt.KeyboardEvent.NUMPAD_ZERO)
          && event.ctrlKey && event.altKey && !event.shiftKey)
  {
    this._diagram.zoomAndCenter();
    dvt.EventManager.consumeEvent(event);
  }
  else if (keyCode == dvt.KeyboardEvent.ENTER || //drill, isolate
      (keyCode == dvt.KeyboardEvent.SPACE && !event.ctrlKey) || // flyout buttons are activated on ENTER or SPACE, but not when both CTRL and SPACE are pressed
      keyCode == dvt.KeyboardEvent.SINGLE_QUOTE || // show additional links
      (keyCode == dvt.KeyboardEvent.SPACE && event.ctrlKey && event.shiftKey) || // open,close
      (dvt.KeyboardEvent.isSemicolon(event)) || //restack, unstack
      (keyCode == dvt.KeyboardEvent.ESCAPE)
  )
  {
    if (navigable && navigable.HandleKeyboardEvent)
    {
      //case when an internal element is selected in node, link or legend
      var focusElement = null;
      if (navigable.getCurrentTabStop)
        focusElement = navigable.getCurrentTabStop();

      if (focusElement && focusElement != navigable && focusElement.handleKeyboardEvent) {
        this.hideTooltip();
        if (!(focusElement instanceof dvt.NavButton)) {
          navigable.resetTabStops();
          if (focusElement.hideKeyboardFocusEffect)
            focusElement.hideKeyboardFocusEffect();
        }
        var displayable = focusElement.getDisplayObj ? focusElement.getDisplayObj() : focusElement;
        focusElement.handleKeyboardEvent(event);
        this.UpdateActiveElement(navigable);
        //show tooltips
        coords = displayable.getDimensions(this.getCtx().getStage()).getCenter();
        pageCoords = this.getCtx().stageToPageCoords(coords.x, coords.y);
        this.ProcessObjectTooltip(event, pageCoords.x, pageCoords.y, focusElement, null);
      }
      else
        navigable.HandleKeyboardEvent(event);
    }
    dvt.EventManager.consumeEvent(event);
  }
  else if ((keyCode == dvt.KeyboardEvent.OPEN_ANGLED_BRACKET || keyCode == dvt.KeyboardEvent.CLOSE_ANGLED_BRACKET) &&
      !event.altKey)
  {
    if (navigable instanceof dvt.AdfDiagramNode) {
      this.hideTooltip();
      var panelCard = this._diagram.getPanelCard(navigable);
      if (panelCard && keyCode == dvt.KeyboardEvent.CLOSE_ANGLED_BRACKET)
        panelCard.displayNextDetailItem();
      else if (panelCard && keyCode == dvt.KeyboardEvent.OPEN_ANGLED_BRACKET)
        panelCard.displayPrevDetailItem();
    }
  }
  else if (keyCode == dvt.KeyboardEvent.FORWARD_SLASH && event.ctrlKey)
  {
    if (navigable instanceof dvt.AdfDiagramNode && navigable.getStateProp) {

      var index = navigable.getStateProp(dvt.AfPanelCard.DISPLAYED_DETAIL_ITEM_INDEX_KEY);
      if (!index)
        index = 0;

      var panelCard = this._diagram.getPanelCard(navigable);
      if (panelCard) {
        var sdiTemp = panelCard.getVisibleChild(index);
        var sdiText = sdiTemp.getText();
        this._diagram.syncPanelCards(null, sdiText, null);
      }
    }
  }
  else if (keyCode == dvt.KeyboardEvent.TAB) {
    eventConsumed = false;
    //check if we have focusable object inside dvt.AdfDiagramNode
    if (navigable instanceof dvt.AdfDiagramNode && navigable.isShowingKeyboardFocusEffect()) {
      var focusable = navigable.getCurrentTabStop();
      if (focusable && focusable != navigable) {
        focusable.hideKeyboardFocusEffect();
      }
      //find next one
      focusable = event.shiftKey ? navigable.getPrevTabStop() : navigable.getNextTabStop();
      if (focusable) {
        if (focusable != navigable) {
          //bug 20838144 fix, removing focus treatment on node when tabbing into the elements of the node
          navigable.hideKeyboardFocusEffect(true);
          //update focus
          var displayable = focusable.getDisplayObj ? focusable.getDisplayObj() : focusable;
          this.UpdateActiveElement(focusable, displayable);
          focusable.showKeyboardFocusEffect();
          //show tooltips
          coords = displayable.getDimensions(this.getCtx().getStage()).getCenter();
        }
        else {
          focusable.showKeyboardFocusEffect(true);
          coords = focusable.getKeyboardBoundingBox(this.getCtx().getStage()).getCenter();
        }
        pageCoords = this.getCtx().stageToPageCoords(coords.x, coords.y);
        this.ProcessObjectTooltip(event, pageCoords.x, pageCoords.y, focusable);
        dvt.EventManager.consumeEvent(event);
        eventConsumed = true;
      }
    }

    if (!eventConsumed) {
      eventConsumed = dvt.AdfDiagramEventManager.superclass.ProcessKeyboardEvent.call(this, event);
    }
  }
  else
  {
    eventConsumed = dvt.AdfDiagramEventManager.superclass.ProcessKeyboardEvent.call(this, event);
  }

  return eventConsumed;
};


/**
 * @override
 */
dvt.AdfDiagramEventManager.prototype.OnKeyUp = function(event) 
{
  var keyCode = event.keyCode;
  var editor = this.GetEditor();
  if (editor) {
    if (keyCode == dvt.KeyboardEvent.DELETE)
    {
      // Create and fire the event
      var selection = this.getSelectionHandler().getSelection();
      var links = [];
      var nodes = [];
      for (var i = 0; i < selection.length; i++) {
        var selected = selection[i];
        var selectedId = selected.getId();
        if (selectedId.charAt(0) == 'L') {
          if (selected.isDeleteEnabled())
            links.push(selectedId);
        } else {
          if (selected.isDeleteEnabled())
            nodes.push(selectedId);
        }
      }
      if (nodes.length > 0) {
        var evt = new dvt.AdfDiagramDeleteNodeEvent(nodes);
        this._callback.call(this._callbackObj, evt);
      }
      if (links.length > 0) {
        var evt = new dvt.AdfDiagramDeleteLinkEvent(links);
        this._callback.call(this._callbackObj, evt);
      }
    }
    else if (keyCode == dvt.KeyboardEvent.L && event.ctrlKey && event.altKey) {
      //link creation
      var navigable = this.getFocus();
      if (navigable instanceof dvt.AdfDiagramNode && this._diagram.isLinkToolEnabled() && navigable.getLinkHandle()) {
        var resultCreate = editor.processLinkCreateStart(navigable.getLinkHandle(), null);
        editor.setLinkCreateWithKeyboard(resultCreate);
        editor.processLinkCreateWithKeyboard(null);
      }
    }
    else if (keyCode == dvt.KeyboardEvent.N && event.ctrlKey && event.altKey) {
      //node creation
      var navigable = this.getFocus();
      if (navigable instanceof dvt.AdfDiagramNode && this._diagram.isStickyNodeEnabled()) {
        editor.processNodeCreateWithKeyboard(navigable);
      }
    }
  }

  if (keyCode == dvt.KeyboardEvent.ESCAPE) {
    this._diagram.HideNodeTransientToolbar();
    if (editor) {
      //cancel link creation if it was started
      editor.processLinkCreateCancel();
    }
  }

  dvt.AdfDiagramEventManager.superclass.OnKeyUp.call(this, event);
};


/**
 * Roll Over event handler
 * @protected
 * @param {dvt.MouseEvent} event the roll over event
 */
dvt.AdfDiagramEventManager.prototype.OnRollOver = function(event) {
  dvt.AdfDiagramEventManager.superclass.OnRollOver.call(this, event);
  var obj = this.GetLogicalObject(this.GetCurrentTargetForEvent(event));

  // Return if no object is found
  if (!obj)
    return;

  // Node/Link highlighting
  if (obj.showRollOverEffect) {
    obj.showRollOverEffect(event);
  }
};


/**
 * Roll Out event handler
 * @protected
 * @param {dvt.MouseEvent} event the roll out event
 */
dvt.AdfDiagramEventManager.prototype.OnRollOut = function(event) {
  dvt.AdfDiagramEventManager.superclass.OnRollOut.call(this, event);
  var obj = this.GetLogicalObject(this.GetCurrentTargetForEvent(event));

  // Return if no object is found
  if (!obj)
    return;

  // Node/Link highlighting
  if (obj.hideRollOverEffect) {
    obj.hideRollOverEffect(event);
  }
};

dvt.AdfDiagramEventManager.prototype.GetEditor = function() {
  return this._diagram.getEditor();
};

dvt.AdfDiagramEventManager.prototype.OnMouseDown = function(event) {
  var editor = this.GetEditor();
  if (editor) {
    var obj = this.GetLogicalObject(this.GetCurrentTargetForEvent(event));
    var relPos = this._context.pageToStageCoords(event.pageX, event.pageY);
    var ret = editor.processDragStart(obj, relPos);
    if (ret.dragStarted)
      this._setPanningEnabled(false);
  }

  dvt.AdfDiagramEventManager.superclass.OnMouseDown.call(this, event);
};

dvt.AdfDiagramEventManager.prototype.OnMouseMove = function(event) {
  //save current mouse info for use with HV hover window
  this._currPageX = event.pageX;
  this._currPageY = event.pageY;
  this._currTarget = event.target;

  var editor = this.GetEditor();
  if (editor) {
    var obj = this.GetLogicalObject(this.GetCurrentTargetForEvent(event));
    var relPos = this._context.pageToStageCoords(event.pageX, event.pageY);
    editor.processDragMove(obj, relPos);
  }
  dvt.AdfDiagramEventManager.superclass.OnMouseMove.call(this, event);
};

dvt.AdfDiagramEventManager.prototype.OnMouseUp = function(event) {
  var editor = this.GetEditor();
  if (editor) {
    var obj = this.GetLogicalObject(this.GetCurrentTargetForEvent(event));
    var relPos = this._context.pageToStageCoords(event.pageX, event.pageY);
    var ret = editor.processDragEnd(obj, relPos);
    var editorEvents = ret.events;
    for (var i = 0; i < editorEvents.length; i++) {
      this._diagram.dispatchEvent(editorEvents[i]);
    }
    this._setPanningEnabled(true);
  }
  dvt.AdfDiagramEventManager.superclass.OnMouseUp.call(this, event);
};


/**
 * @override
 */
dvt.AdfDiagramEventManager.prototype.OnMouseOut = function(event) {
  if (event.target != null && event.relatedTarget == null) {
    var editor = this.GetEditor();
    if (editor) {
      // do the necessary cleanup
      var obj = this.GetLogicalObject(this.GetCurrentTargetForEvent(event));
      var relPos = this._context.pageToStageCoords(event.pageX, event.pageY);
      var ret = editor.processDragCancel(obj, relPos);
      var editorEvents = ret.events;
      for (var i = 0; i < editorEvents.length; i++) {
        this._diagram.dispatchEvent(editorEvents[i]);
      }
    }
    // simulate mouse up event
    this.OnMouseUp(event);
  }

  //To handle a scenario when a user moves from a node to the node endpoint :
  //the node is going to remove hover effect but node endpoint should stay.
  var curObj = this.GetLogicalObject(this.GetCurrentTargetForEvent(event));
  var enteredObj = this.GetLogicalObject(event.relatedTarget);
  if (enteredObj instanceof DvtAdfDiagramNodeEndpoint && enteredObj.getParent() == curObj)
    enteredObj.setHovered(true);

  dvt.AdfDiagramEventManager.superclass.OnMouseOut.call(this, event);
};

dvt.AdfDiagramEventManager.GetTempLinkPositionOnNode = function(node, targetItem)
{
  if (node && node.getLayoutBounds) {
    var nodeDim = node.getLayoutBounds();
    var center = new dvt.Point(nodeDim.x + nodeDim.w / 2, nodeDim.y + nodeDim.h / 2);
    var centerPoint = node.localToStage(center);
    return targetItem.stageToLocal(centerPoint);
  }
  return null;
};
dvt.AdfDiagramEventManager.GetParentNode = function(obj)
{
  var parent = obj;
  while (parent)
  {
    if (parent instanceof dvt.AdfDiagramNode)
    {
      return parent;
    }
    if (!parent.getParent)
      break;
    parent = parent.getParent();
  }
};
dvt.AdfDiagramEventManager.GetCreateNodeEvent = function(parentId, relPos, paletteId) {
  var evt = new dvt.AdfDiagramCreateNodeEvent(paletteId, parentId, relPos);
  return evt;
};


/**
 * Click event handler.
 * @protected
 */
dvt.AdfDiagramEventManager.prototype.OnClick = function(event) {
  var obj = this.GetLogicalObject(this.GetCurrentTargetForEvent(event));
  var nodeCreatePerformed = false;
  var editor = this.GetEditor();
  if (editor) {
    var relPos = this._context.pageToStageCoords(event.pageX, event.pageY);
    var ret = editor.processNodeCreate(obj, relPos);
    var editorEvents = ret.events;
    if (editorEvents.length > 0) {
      for (var i = 0; i < editorEvents.length; i++) {
        this._diagram.dispatchEvent(editorEvents[i]);
      }
      nodeCreatePerformed = true;
    }
  }
  if (!nodeCreatePerformed) {
    dvt.AdfDiagramEventManager.superclass.OnClick.call(this, event);
    // Done if there is no object
    if (!obj)
      return;

    if (obj.HandleClick) {
      obj.HandleClick(event);
    }
  }
};


/**
 * Double click event handler.
 * @protected
 */
dvt.AdfDiagramEventManager.prototype.OnDblClickInternal = function(event) {
  var obj = this.GetLogicalObject(this.GetCurrentTargetForEvent(event));

  // Done if there is no object
  if (!obj)
    return;

  if (obj.isDoubleClickable && obj.isDoubleClickable() && obj.HandleDblClick) {
    obj.HandleDblClick(event);
  }
};


/**
 * Returns the menu type to display.
 * @param {object} logicalObj Logical object used to retrieve the correct selection handler.
 * @return {object} The menu type to pass into the context menu handler.
 * @override
 * @protected
 */
dvt.AdfDiagramEventManager.prototype.GetContextMenuType = function(logicalObj) {
  // Figure out the selection size, which determines which menu to display
  var selectionHandler = this.getSelectionHandler();
  var selectionSize = selectionHandler ? selectionHandler.getSelection().length : 0;
  if (selectionSize <= 0)
    return dvt.ContextMenuHandler.TYPE_BODY_CONTEXT_MENU;
  else if (selectionSize == 1) {
    // Node or Link?
    var isLink = selectionHandler.getSelection()[0].getId().charAt(0) == 'L';
    return isLink ? dvt.AdfDiagramEventManager.TYPE_LINK_CONTEXT_MENU : dvt.ContextMenuHandler.TYPE_CONTEXT_MENU;
  }
  else
    return dvt.ContextMenuHandler.TYPE_MULTI_SELECT_CONTEXT_MENU;
};


/**
 * @override
 */
dvt.AdfDiagramEventManager.prototype.ShowKeyboardFocusByDefault = function() 
{
  return this._showKeyboardFocusByDefault && !dvt.Agent.isTouchDevice();
};

dvt.AdfDiagramEventManager.prototype.setShowKeyboardFocusByDefault = function(showKeyboardFocusByDefault) {
  this._showKeyboardFocusByDefault = showKeyboardFocusByDefault;
};

dvt.AdfDiagramEventManager.TYPE_LINK_CONTEXT_MENU = 'linkContextMenu';

dvt.AdfDiagramEventManager.prototype.CreateClientBehaviorHandler = function(context, callback, callbackObj) {
  return this._diagram.CreateClientBehaviorHandler(context, callback, callbackObj);
};

dvt.AdfDiagramEventManager.prototype.isClearMenuAllowed = function() {
  return false;
};


/**
 * Processes the client behavior event for touch device
 *
 * @override
 * @param {object} obj The target of event.
 * @param {array} clientBehaviors The array of applicable client behaviors.
 * @return {boolean} true if a client behavior was triggered, false otherwise
 */
dvt.AdfDiagramEventManager.prototype.ProcessClientBehaviorForTouch = function(obj, clientBehaviors) {
  //Bug 17369778: ClickBehaviorHandler always hides the flyouts and resets the transientToolbarDataId
  //retain the transient toolbar id after handling click behavior.
  //This is required to toggle flyouts in diagram components for touch device
  var transientToolbarDataId = this._diagram.GetTransientToolbarDataId();
  var consumed = this.ClientBehaviorHandler.processClick(obj, clientBehaviors);
  this._diagram.SetTransientToolbarDataId(transientToolbarDataId);

  return consumed;
};


/**
 * @override
 */
dvt.AdfDiagramEventManager.prototype.HandleTouchClickInternal = function(event) {
  var consumed = this.GetEventInfo(event, dvt.PanZoomCanvasEventManager.EVENT_INFO_PANNED_KEY);
  if (consumed)
    return true;

  var obj = this.GetLogicalObject(this.GetCurrentTargetForEvent(event));
  if (obj && obj.HandleClick)
    obj.HandleClick(event);
};


/**
 * @override
 */
dvt.AdfDiagramEventManager.prototype.HandleTouchDblClickInternal = function(event) {
  var obj = this.GetLogicalObject(this.GetCurrentTargetForEvent(event));
  // Done if there is no object
  if (!obj)
    return;
  if (obj.isDoubleClickable && obj.isDoubleClickable() && obj.HandleDblClick) {
    obj.HandleDblClick(event);
  }
};


/**
 * @override
 */
dvt.AdfDiagramEventManager.prototype.HandleImmediateTouchStartInternal = function(event) {
  var targetObj = event.target;
  var swipeTarget = this._findSwipeTarget(targetObj);
  if (swipeTarget) {
    this.TouchManager.processAssociatedTouchAttempt(event, dvt.AdfDiagramEventManager.SWIPE_KEY, this.SwipeStartTouch, this);
  }

  var editor = this.GetEditor();
  if (editor && event.targetTouches.length == 1) {
    var obj = this.GetLogicalObject(this.GetCurrentTargetForEvent(event));
    var touch = event.targetTouches[0];
    var relPos = this._context.pageToStageCoords(touch.pageX, touch.pageY);
    var ret = editor.processDragStart(obj, relPos);
    this._touchId = null;
    if (ret.dragStarted) {
      this._touchId = touch.identifier;
      this._setPanningEnabled(false);
    }
  }
};


/**
 * @override
 */
dvt.AdfDiagramEventManager.prototype.HandleImmediateTouchMoveInternal = function(event) {
  this.TouchManager.processAssociatedTouchMove(event, dvt.AdfDiagramEventManager.SWIPE_KEY);

  var editor = this.GetEditor();
  if (editor && this._touchId !== null && typeof this._touchId !== 'undefined') {
    var touch = dvt.TouchManager.getTouchById(this._touchId, event.touches);
    var obj = this.GetLogicalObject(this.GetCurrentTargetForEvent(event));
    var relPos = this._context.pageToStageCoords(touch.pageX, touch.pageY);
    editor.processDragMove(obj, relPos);
  }
};


/**
 * @override
 */
dvt.AdfDiagramEventManager.prototype.HandleImmediateTouchEndInternal = function(event) {
  this.TouchManager.processAssociatedTouchEnd(event, dvt.AdfDiagramEventManager.SWIPE_KEY);

  var editor = this.GetEditor();
  if (editor && this._touchId !== null && typeof this._touchId !== 'undefined') {
    var touch = dvt.TouchManager.getTouchById(this._touchId, event.changedTouches);

    var obj = null;
    var documentUtils = this._context.getDocumentUtils();
    if (documentUtils)
      obj = this.GetLogicalObject(documentUtils.elementFromTouch(touch));

    var relPos = this._context.pageToStageCoords(touch.pageX, touch.pageY);
    var ret = editor.processDragEnd(obj, relPos);
    var editorEvents = ret.events;
    for (var i = 0; i < editorEvents.length; i++) {
      this._diagram.dispatchEvent(editorEvents[i]);
    }
    this._setPanningEnabled(true);
    this._touchId = null;
  }
};

dvt.AdfDiagramEventManager.prototype._findSwipeTarget = function(target) {
  while (target) {
    var obj = this.GetLogicalObject(target);
    if (obj) {
      if (obj.isSwipeTarget && obj.isSwipeTarget()) {
        return obj;
      }
      if (!this.IsPropagationEnabled(null, event, target)) {
        return null;
      }
    }
    target = target.getParent();
  }
};

dvt.AdfDiagramEventManager.prototype.SwipeStartTouch = function(event, touch) {
  var touchIds = this.TouchManager.getTouchIdsForObj(dvt.AdfDiagramEventManager.SWIPE_KEY);
  if (touchIds.length <= 1) {
    this.TouchManager.saveProcessedTouch(touch.identifier, dvt.AdfDiagramEventManager.SWIPE_KEY, null, dvt.AdfDiagramEventManager.SWIPE_KEY, dvt.AdfDiagramEventManager.SWIPE_KEY, this.SwipeMoveTouch, this.SwipeEndTouch, this);
    this.TouchManager.setTooltipEnabled(touch.identifier, false);
    var pos = this._context.pageToStageCoords(touch.pageX, touch.pageY);
    this._swipeStartPos = pos;

    // see if we need to disable panning by checking to see if we're over an object that handles swipes
    var target = event.target;
    while (target) {
      var obj = this.GetLogicalObject(target);
      if (obj) {
        if (obj.handleSwipe) {
          this._setPanningEnabled(false);
          break;
        }
        if (!this.IsPropagationEnabled(null, event, target))
          break;
      }
      target = target.getParent();
    }
  }

  if (event) {
    event.preventDefault();
  }
};

dvt.AdfDiagramEventManager.prototype.SwipeMoveTouch = function(event, touch) {
  if (event) {
    event.preventDefault();
  }
};

dvt.AdfDiagramEventManager.prototype.SwipeEndTouch = function(event, touch) {
  var endPos = this._context.pageToStageCoords(touch.pageX, touch.pageY);
  if (event) {
    this._handleSwipe(event.target, endPos.x - this._swipeStartPos.x, endPos.y - this._swipeStartPos.y);
    event.preventDefault();
  }
  this._swipeStartPos = null;

  // enable panning after handling swipe
  this._setPanningEnabled(true);
};

dvt.AdfDiagramEventManager.prototype._handleSwipe = function(target, deltaX, deltaY) {
  while (target) {
    var obj = this.GetLogicalObject(target);
    if (obj) {
      if (obj.handleSwipe && obj.handleSwipe(deltaX, deltaY)) {
        return;
      }
      if (!this.IsPropagationEnabled(null, event, target)) {
        return;
      }
    }
    target = target.getParent();
  }
};

dvt.AdfDiagramEventManager.prototype.IsDragCandidate = function(obj) {
  return obj instanceof dvt.AdfDiagramNode;
};

dvt.AdfDiagramEventManager.prototype.getCurrentStageMousePosition = function() {
  return this._context.pageToStageCoords(this._currPageX, this._currPageY);
};

dvt.AdfDiagramEventManager.prototype.getCurrentMouseTarget = function() {
  return this._currTarget;
};

dvt.AdfDiagramEventManager.prototype._setPanningEnabled = function(bEnablePanning) {
  if (this._diagram.IsPanningEnabled())
    this._diagram.getPanZoomCanvas().setPanningEnabled(bEnablePanning);
};


/**
 * @override
 */
dvt.AdfDiagramEventManager.prototype.OnTouchStartBubble = function(event) {
  dvt.AdfDiagramEventManager.superclass.OnTouchStartBubble.call(this, event);
  if (this.DragSource && !event.dragSourceSet) {
    var displayable = event.target;
    while (displayable) {
      var logicalObject = this.GetLogicalObject(displayable, true);
      if (logicalObject && this.IsDragCandidate(logicalObject)) {
        this.DragSource.setDragCandidate(logicalObject);
        event.dragSourceSet = true;
        break;
      }
      displayable = displayable.getParent();
    }
  }
};

/**
 * @override
 */
dvt.AdfDiagramEventManager.prototype.GetTouchResponse = function() {
  var options = this._diagram.getOptions();
  if (options['panning'] !== 'none' || options['zooming'] !== 'none')
    return dvt.EventManager.TOUCH_RESPONSE_TOUCH_HOLD;
  else if (options['touchResponse'] === dvt.EventManager.TOUCH_RESPONSE_TOUCH_START)
    return dvt.EventManager.TOUCH_RESPONSE_TOUCH_START;
  return dvt.EventManager.TOUCH_RESPONSE_AUTO;
};

/**
 * @override
 */
dvt.AdfDiagramEventManager.prototype.StoreInfoByEventType = function(key) {
  if (key == dvt.PanZoomCanvasEventManager.EVENT_INFO_PANNED_KEY) {
    return false;
  }
  return dvt.AdfDiagramEventManager.superclass.StoreInfoByEventType.call(this, key);
};

/**
 * @override
 */
dvt.AdfDiagramEventManager.prototype.ProcessRolloverEvent = function(event, obj, bOver) {
  //Only called for keyboard events
  if (!(event instanceof dvt.KeyboardEvent)) {
    return;
  }
  if (bOver && obj.showRollOverEffect) {
    obj.showRollOverEffect(event);
  }
  else if (!bOver && obj.hideRollOverEffect) {
    obj.hideRollOverEffect(event);
  }
};
/*---------------------------------------------------------------------------------*/
/*  DvtAdfDiagramKeyboardHandler     Keyboard handler for Diagram                     */
/*---------------------------------------------------------------------------------*/
/**
  *  @param {dvt.AdfDiagram} component The owning diagram component
  *  @param {dvt.EventManager} manager The owning dvt.EventManager
  *  @class DvtAdfDiagramKeyboardHandler
  *  @extends {dvt.BaseDiagramKeyboardHandler}
  *  @constructor
  */
var DvtAdfDiagramKeyboardHandler = function(component, manager)
{
  this.Init(component, manager);
};

dvt.Obj.createSubclass(DvtAdfDiagramKeyboardHandler, dvt.BaseDiagramKeyboardHandler, 'DvtAdfDiagramKeyboardHandler');


/**
 * @override
 */
DvtAdfDiagramKeyboardHandler.prototype.isSelectionEvent = function(event)
{
  if (event.keyCode == dvt.KeyboardEvent.TAB)
    return false;
  else
    return this.isNavigationEvent(event) && !event.ctrlKey;
};


/**
 * @override
 */
DvtAdfDiagramKeyboardHandler.prototype.processKeyDown = function(event) {
  var keyCode = event.keyCode;
  var currentNavigable = this._eventManager.getFocus();
  if (keyCode == dvt.KeyboardEvent.F && event.ctrlKey) {
    if (currentNavigable && currentNavigable.hideKeyboardFocusEffect) {
      currentNavigable.hideKeyboardFocusEffect();
    }
    this.GetDiagram().FocusSearchField();
    dvt.EventManager.consumeEvent(event);
    return null;
  } else if (((keyCode >= dvt.KeyboardEvent.ONE && keyCode <= dvt.KeyboardEvent.FIVE) ||
      (keyCode >= dvt.KeyboardEvent.NUMPAD_ONE && keyCode <= dvt.KeyboardEvent.NUMPAD_FIVE))
      && !event.ctrlKey && !event.altKey) {
    //Zoom to 10%, 25%, 50%, 75%, 100% when number 1,2,3,4,5 are pressed respectively
    var pct;
    switch (keyCode) {
      case dvt.KeyboardEvent.ONE:
      case dvt.KeyboardEvent.NUMPAD_ONE: pct = 0.10; break;
      case dvt.KeyboardEvent.TWO:
      case dvt.KeyboardEvent.NUMPAD_TWO: pct = 0.25; break;
      case dvt.KeyboardEvent.THREE:
      case dvt.KeyboardEvent.NUMPAD_THREE: pct = 0.50; break;
      case dvt.KeyboardEvent.FOUR:
      case dvt.KeyboardEvent.NUMPAD_FOUR: pct = 0.75; break;
      case dvt.KeyboardEvent.FIVE:
      case dvt.KeyboardEvent.NUMPAD_FIVE: pct = 1.00; break;
    }
    //set zoom scale based on zoom percent
    var animator = new dvt.Animator(this.GetDiagram().getCtx(), this.GetDiagram().getAnimationDuration());
    this.GetDiagram().getPanZoomCanvas().zoomTo(pct, null, null, animator);
    animator.play();
    dvt.EventManager.consumeEvent(event);
    return null;
  } else if (((keyCode >= dvt.KeyboardEvent.ONE && keyCode <= dvt.KeyboardEvent.FIVE) ||
      (keyCode >= dvt.KeyboardEvent.NUMPAD_ONE && keyCode <= dvt.KeyboardEvent.NUMPAD_FIVE)) &&
      event.ctrlKey && event.altKey) { //Num Key
    //Switch Layout when number keys are pressed along with control & alt keys
    if (this.GetDiagram().HandleKeyboardEvent) {
      this.GetDiagram().HandleKeyboardEvent(event);
      dvt.EventManager.consumeEvent(event);
      return null;
    }
  } else if (keyCode == dvt.KeyboardEvent.HOME && event.ctrlKey) {
    //In HV, HOME + ctrl will clear current selection and selects & focuses the root node
    if (this.GetDiagram().HandleKeyboardEvent) {
      var nextNavigable = this.GetDiagram().HandleKeyboardEvent(event);
      if (nextNavigable) {
        this._eventManager.ProcessSelectionEventHelper(nextNavigable, event.shiftKey);
        return nextNavigable;
      }
    }
  } else if (event.keyCode == dvt.KeyboardEvent.SIX && event.shiftKey && event.ctrlKey) {
    //Go one level up on active node when Ctrl + Shift + 6 is pressed (Shift + 6 is ^)
    if (currentNavigable instanceof dvt.AdfDiagramNode) {
      var nextNavigable = currentNavigable.getNextNavigable(event);
      if (nextNavigable) {
        this._eventManager.getSelectionHandler().clearSelection();
        return nextNavigable;
      }
    }
  } else if (keyCode == dvt.KeyboardEvent.SINGLE_QUOTE) {
    if (currentNavigable instanceof dvt.AdfDiagramNode) {
      var node = currentNavigable;
      if (node && this.GetDiagram().isNodeDetail() && this.GetDiagram()._detailNode != node) {
        this.GetDiagram()._bKeyboardHoverDetails = true;
        node.showHoverDetails(true);
        this.GetDiagram()._bKeyboardHoverDetails = false;
      }
    }
  }
  else if (keyCode == dvt.KeyboardEvent.ESCAPE) {
    if (this.GetDiagram()._detailNode) {
      this.GetDiagram()._detailNode.hideHoverDetails();
    }
  }
  else if (keyCode == dvt.KeyboardEvent.TAB) {
    var next = null;
    dvt.EventManager.consumeEvent(event);
    if (!currentNavigable) {
      next = this.GetDiagram().GetInitialFocusNode();
    }
    else {
      next = currentNavigable;
    }
    return next;
  }
  else if (this.isNavigationEvent(event) && currentNavigable instanceof dvt.AdfDiagramNode) {
    var focusable = currentNavigable.getCurrentTabStop();
    if (focusable && focusable != currentNavigable) {
      dvt.EventManager.consumeEvent(event);
      return null;
    }
  }
  return DvtAdfDiagramKeyboardHandler.superclass.processKeyDown.call(this, event);
};

/**
 * Check if the keyboard event is navigation event
 * @param {dvt.KeyboardEvent} event  Dvt keyboard event
 * @return {boolean} true if the event is navigation event
 */
DvtAdfDiagramKeyboardHandler.prototype.isNavigationEvent = function(event)
{
  var retVal = false;
  switch (event.keyCode) {
    case dvt.KeyboardEvent.OPEN_BRACKET:
    case dvt.KeyboardEvent.CLOSE_BRACKET:
    case dvt.KeyboardEvent.TAB:
    case dvt.KeyboardEvent.HOME:
    case dvt.KeyboardEvent.END:
      retVal = true; break;
    case dvt.KeyboardEvent.OPEN_ANGLED_BRACKET:
    case dvt.KeyboardEvent.CLOSE_ANGLED_BRACKET:
      retVal = event.altKey ? true : false;
      break;
    default:
      retVal = DvtAdfDiagramKeyboardHandler.superclass.isNavigationEvent.call(this, event);
  }
  return retVal;
};

/**
 * @override
 */
DvtAdfDiagramKeyboardHandler.prototype.GetVisibleNode = function(nodeId) {
  var node;
  // check if a node is a part of a collapsed container
  do {
    if (!nodeId)
      break;
    node = this.GetDiagram().getNodeById(nodeId);
    nodeId = node.getData().getGroupId();
  } while (!node.getVisible());

  // check if a node is a part of a stack
  if (node && !node.getVisible()) {
    var stackNodeMap = this.GetDiagram().GetStackNodeMap();
    var arNodeIds;
    if (stackNodeMap)
      arNodeIds = stackNodeMap[node.GetStackId()];
    if (arNodeIds && arNodeIds.length > 0) {
      for (var i = 0; i < arNodeIds.length; i++) {
        var testNode = this.GetDiagram().getNodeById(arNodeIds[i]);
        if (testNode.getVisible()) {
          node = testNode;
          break;
        }
      }
    }
  }
  return node;
};
// Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
/**
 * @constructor
 */
var DvtAdfDiagramStackBadgeHandler = function(diagram, node)
{
  this.Init(diagram, node);
};

dvt.Obj.createSubclass(DvtAdfDiagramStackBadgeHandler, dvt.Obj, 'DvtAdfDiagramStackBadgeHandler');

DvtAdfDiagramStackBadgeHandler.prototype.Init = function(diagram, node)
{
  this._diagram = diagram;
  this._node = node;

  this._dialog;
  this._layoutContainer;
  this._buttonContainer;
  this._arNodes;
  this._isDialogVisible = false;
};

DvtAdfDiagramStackBadgeHandler.prototype.getTooltip = function()
{
  var format = this._diagram.getResourcesMap()['stackIndicatorTip'];
  var s = dvt.Bundle.format(format, [this._node.getStackCount()]);
  return s;
};

//Bug 13381888: clicking on stack badge shouldn't affect selection
DvtAdfDiagramStackBadgeHandler.prototype.isUnrelatedToSelection = function()
{
  return true;
};

DvtAdfDiagramStackBadgeHandler.prototype.ShowDialog = function(event, bImmediate)
{
  var stackNodeMap = this._diagram.GetStackNodeMap();
  var nodeStackMap = this._diagram.GetNodeStackMap();

  if (!(stackNodeMap && nodeStackMap))
  {
    return;
  }

  var stackId = nodeStackMap[this._node.getData().getId()];
  var arNodeIds = stackNodeMap[stackId];
  if (!arNodeIds || arNodeIds.length < 1)
  {
    return;
  }

  //Bug 15838158: store the node id on the Diagram
  //Bug 15838442: store the node id on the Diagram before
  //creating copies of the nodes and rendering them, so that the
  //copies won't affect the overview window
  this._diagram.SetStackPreviewNodeId(this._node.getData().getId());
  this._diagram.UpdateAriaAttributes();

  var layoutContainer = new DvtAdfDiagramLayoutContainer(this._diagram.getCtx(), this._diagram);
  layoutContainer.setMouseEnabled(false);
  this._diagram.getPanZoomCanvas().getContentPane().addChild(layoutContainer);
  var arNodes = this.CreateNodes(arNodeIds);
  this._arNodes = arNodes;
  var i;
  //add nodes to display list before rendering them
  for (i = 0; i < arNodes.length; i++)
  {
    layoutContainer.addNode(arNodes[i]);
  }

  //render nodes once at 100%
  this.RenderNodes(arNodes);

  //enable zoom facet switching again
  for (i = 0; i < arNodes.length; i++)
  {
    arNodes[i].setForceZoom100Facet(false);
  }

  layoutContainer.layout();

  //start the rect above the viewport and increase its height so that
  //we can bounce the animation easing and not see the top edge of the rect
  var rect = new dvt.Rect(this._diagram.getCtx(), 0, -.5 * this._diagram.getHeight(), this._diagram.getWidth(), 1.5 * this._diagram.getHeight(), 'stackPreviewBackground');
  var style = this._diagram.getStackPreviewColor();
  var color = dvt.AdfDiagramNode._STACK_PREVIEW_COLOR;
  var alpha = dvt.AdfDiagramNode._STACK_PREVIEW_ALPHA;
  if (style) {
    color = dvt.ColorUtils.getRGB(style) ? dvt.ColorUtils.getRGB(style) : dvt.AdfDiagramNode._STACK_PREVIEW_COLOR;
    alpha = dvt.ColorUtils.getAlpha(style) ? dvt.ColorUtils.getAlpha(style) : dvt.AdfDiagramNode._STACK_PREVIEW_ALPHA;
  }
  var fill = new dvt.SolidFill(color, alpha);
  rect.setFill(fill);
  rect.addChild(layoutContainer);
  this._diagram.addChild(rect);
  //clip the preview so that any nodes that don't fit within the viewport
  //are not visible when the preview animates in or out of view
  var clipPath = new dvt.ClipPath('stackPreviewClip');
  clipPath.addRect(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight());
  layoutContainer.setClipPath(clipPath);

  //Don't render the buttons for non browser environment which is meant for printing
  if (dvt.Agent.isEnvironmentBrowser()) {

    this._buttonContainer = new dvt.Container(this._diagram.getCtx());
    //temporarily add buttonContainer to display list so we can get dimensions
    this._diagram.addChild(this._buttonContainer);

    var pinButton = this.CreatePinButton();
    this._buttonContainer.addChild(pinButton);
    var pinButtonDims = pinButton.getDimensions();

    var closeButton = this.CreateCloseButton();
    this._buttonContainer.addChild(closeButton);
    var closeButtonDims = closeButton.getDimensions();

    var interButtonPadding = 7;
    var buttonStartX = (.5 * (this._diagram.getWidth() - (pinButtonDims.x + pinButtonDims.w + interButtonPadding + closeButtonDims.x + closeButtonDims.w)));
    pinButton.setTranslate(buttonStartX, -this._diagram._buttonBorderRadius);
    closeButton.setTranslate(pinButton.getTranslateX() + pinButtonDims.x + pinButtonDims.w + interButtonPadding, -this._diagram._buttonBorderRadius);
  }

  this._dialog = rect;
  this._layoutContainer = layoutContainer;

  this._diagram.getPanZoomCanvas().addEvtListener(dvt.ZoomEvent.TYPE, this.HandleZoom, false, this);

  this.SetupPanZoomCanvas();
  if (this._buttonContainer) {
    //add buttons in front of preview so that nodes pan around behind them
    this._diagram.addChild(this._buttonContainer);
  }

  //Bug 15867162: set the layout container as the contentPane so we can zoom-to-fit it; it will later get
  //re-parented to the panZoomCanvas in InAnimOnEnd
  //(NOTE: the zoom listener must have already been added above in order for the nodes to get re-rendered at the
  //correct zoom level after the zoom-to-fit)
  this._diagram.getPanZoomCanvas().setContentPane(this._layoutContainer);
  this._diagram.getPanZoomCanvas().zoomToFit();

  //Bug 15838158: handle showing the preview immediately after a component resize
  if (!bImmediate) {
    var dialogAnim = this.CreateDialogInAnim();
    dialogAnim.play();
  }
  else {
    //need to do everything the anim would have done, except for the actual animation
    this.InAnimOnEnd();
  }
  this._isDialogVisible = true;

  //Set the state of preview stack
  var propEvent = dvt.EventFactory.newAdfPropertyChangeEvent(dvt.AdfDiagram.PREVIEW_ROW_KEY, this._node.getData().getId());
  this._diagram.dispatchEvent(propEvent);
};

DvtAdfDiagramStackBadgeHandler.prototype.ApplyLayoutPanZoom = function(panZoom) {
  if (this._layoutContainer && panZoom) {
    var pzc = this._layoutContainer._diagram.getPanZoomCanvas();
    pzc.zoomTo(panZoom.zoom, 0, 0);
    pzc.panTo(panZoom.panX, panZoom.panY);
  }
};

DvtAdfDiagramStackBadgeHandler.prototype.SetupPanZoomCanvas = function() {
  var pzc = this._diagram.getPanZoomCanvas();
  this._panZoomCanvasTranslateY = pzc.getTranslateY();
  this._panZoomCanvasSize = pzc.getSize();
  this._contentPane = pzc.getContentPane();
  this._contentPaneParent = this._contentPane.getParent();
  this._contentPaneIndex = this._contentPaneParent.getChildIndex(this._contentPane);
  this._panZoomCanvasParent = pzc.getParent();
  this._panZoomCanvasIndex = this._panZoomCanvasParent.getChildIndex(pzc);
  //add the old content pane in the same z-order as the pzc used to be
  this._panZoomCanvasParent.addChildAt(this._contentPane, this._panZoomCanvasIndex);
  //bring the pzc to the front
  this._diagram.addChild(pzc);
  this._contentPaneParent.addChildAt(this._dialog, this._contentPaneIndex);
  //set the translateY and size of pzc
  pzc.setTranslateY(0);
  pzc.setSize(this._panZoomCanvasSize.w, this._panZoomCanvasSize.h + this._panZoomCanvasTranslateY);
};

DvtAdfDiagramStackBadgeHandler.prototype.RestorePanZoomCanvas = function() {
  var pzc = this._diagram.getPanZoomCanvas();
  this._contentPaneParent.addChildAt(this._contentPane, this._contentPaneIndex);
  pzc.setContentPane(this._contentPane);
  this._panZoomCanvasParent.addChildAt(pzc, this._panZoomCanvasIndex);
  pzc.setTranslateY(this._panZoomCanvasTranslateY);
  pzc.setSize(this._panZoomCanvasSize.w, this._panZoomCanvasSize.h);

  this._contentPane = null;
  this._contentPaneParent = null;
  this._contentPaneIndex = null;
  this._panZoomCanvasParent = null;
  this._panZoomCanvasIndex = null;
  this._panZoomCanvasTranslateY = null;
  this._panZoomCanvasSize = null;
};

DvtAdfDiagramStackBadgeHandler.prototype.CreateDialogInAnim = function() {
  this._dialog.setTranslateY(-this._diagram.getHeight() - 1);
  var dialogIn;
  if (this._buttonContainer) {
    this._buttonContainer.setTranslateY(-this._diagram.getHeight() - 1);
    dialogIn = new dvt.AnimMoveTo(this._diagram.getCtx(), [this._dialog, this._buttonContainer], new dvt.Point(0, 0), this._diagram.getAnimationDuration());
  } else {
    dialogIn = new dvt.AnimMoveTo(this._diagram.getCtx(), [this._dialog], new dvt.Point(0, 0), this._diagram.getAnimationDuration());
  }
  dialogIn.setEasing(dvt.BaseAnimation.EASING_BOUNCE_OUT);

  dvt.Playable.appendOnEnd(dialogIn, this.InAnimOnEnd, this);

  return dialogIn;
};

DvtAdfDiagramStackBadgeHandler.prototype.InAnimOnEnd = function() {
  var pzc = this._diagram.getPanZoomCanvas();
  //separate out the layout container so we can use it as the contentPane
  pzc.addChildAt(this._layoutContainer, this._contentPaneIndex + 1);
};

DvtAdfDiagramStackBadgeHandler.prototype.CreateDialogCloseAnim = function() {
  var dialogOut;
  if (this._buttonContainer) {
    dialogOut = new dvt.AnimMoveTo(this._diagram.getCtx(), [this._dialog, this._buttonContainer], new dvt.Point(0, -this._diagram.getHeight() - 1), this._diagram.getAnimationDuration());
  } else {
    dialogOut = new dvt.AnimMoveTo(this._diagram.getCtx(), [this._dialog], new dvt.Point(0, -this._diagram.getHeight() - 1), this._diagram.getAnimationDuration());
  }
  dialogOut.setEasing(dvt.BaseAnimation.EASING_BOUNCE_IN);

  dvt.Playable.appendOnEnd(dialogOut, this.RestorePanZoomCanvas, this);
  //add the layout container back to the dialog so that it will animate with it
  this._dialog.addChild(this._layoutContainer);

  return dialogOut;
};

/**
 * Creates a function to be called at the end of close preview dialog
 * @param {boolean} bExplodeStack true to explode stack when dreview dialog closed
 * @return {function} a function to be called at the end of close preview dialog
 * @protected
 */
DvtAdfDiagramStackBadgeHandler.prototype.CreateDialogOutAnimEndFunc = function(bExplodeStack)
{
  var thisRef = this;
  var func = function() {
    //turn off stack preview mode before calling the onEndFunc
    //Bug 15838158: clear the preview node id stored on the Diagram
    thisRef._diagram.SetStackPreviewNodeId(null);
    thisRef._diagram.UpdateAriaAttributes();


    // Bug 14813675 - tooltips get stuck for isolate button and other buttons
    var tooltipManager = thisRef._diagram.getCtx().getTooltipManager();
    if (tooltipManager) {
      tooltipManager.hideTooltip();
    }

    //need to remove button container separately, because it's a child of the stage
    if (thisRef._buttonContainer && thisRef._buttonContainer.getParent()) {
      thisRef._buttonContainer.getParent().removeChild(thisRef._buttonContainer);
    }
    if (thisRef._layoutContainer && thisRef._layoutContainer.getParent())
    {
      thisRef._layoutContainer.getParent().removeChild(thisRef._layoutContainer);
    }
    if (thisRef._dialog && thisRef._dialog.getParent())
    {
      thisRef._dialog.getParent().removeChild(thisRef._dialog);
    }
    thisRef._dialog = null;
    thisRef._layoutContainer = null;
    thisRef._arNodes = null;
    if (bExplodeStack) {
      thisRef.ExplodeStack();
    }
  };
  return func;
};

/**
 * Closes preview dialog and explodes a stack if requested
 * @param {boolean} bExplodeStack true to explode stack when preview dialog closed
 * @param {boolean} bImmediate true to skip animation
 * @protected
 */
DvtAdfDiagramStackBadgeHandler.prototype.CloseDialog = function(bExplodeStack, bImmediate)
{
  this._diagram.getPanZoomCanvas().removeEvtListener(dvt.ZoomEvent.TYPE, this.HandleZoom, false, this);

  //Bug 15838158: handle closing the preview immediately when the component is resized
  if (!bImmediate) {
    var anim = this.CreateDialogCloseAnim();
    var func = this.CreateDialogOutAnimEndFunc(bExplodeStack);
    dvt.Playable.appendOnEnd(anim, func);
    anim.play();
  }
  else {
    //need to do everything that the anims would have done, except for the actual animations
    this._dialog.addChild(this._layoutContainer);
    this.RestorePanZoomCanvas();
    this.CreateDialogOutAnimEndFunc(bExplodeStack).call();
  }
  this._isDialogVisible = false;
};

DvtAdfDiagramStackBadgeHandler.prototype.ExplodeStack = function() {
  var stackId = this._node.GetStackId();
  if (stackId) {
    var arStackNames = this._diagram.getExplodedStacks();
    if (!arStackNames) {
      arStackNames = [];
    }
    else {
      arStackNames = arStackNames.slice(0);
    }
    arStackNames.push(stackId);
    this._diagram.setExplodedStacks(arStackNames);
  }
};

/**
 * Creates the close button which is shown after stack preview
 * @protected
 * @return {dvt.Button} The diagram close button
 */
DvtAdfDiagramStackBadgeHandler.prototype.CreateCloseButton = function()
{
  var iconEna = this._diagram.getResourcesMap()['stackPreviewCloseEna'];
  var iconOvr = this._diagram.getResourcesMap()['stackPreviewCloseOvr'];
  var iconDwn = this._diagram.getResourcesMap()['stackPreviewCloseDwn'];
  var tooltip = this._diagram.getResourcesMap()['stackPreviewCloseTip'];
  var closeButton = this._diagram.CreateButton(iconEna, iconOvr, iconDwn, dvt.AdfDiagram._WIDE_BUTTON_SIZE, dvt.AdfDiagram._BUTTON_SIZE + this._diagram._buttonBorderRadius, this._diagram._buttonBorderRadius);
  var closeButtonHandler = {};
  closeButtonHandler.getTooltip = function() {
    return tooltip;
  };
  var thisRef = this;
  closeButtonHandler.HandleClick = function(evt) {
    //Bug 13381888: consume the event
    dvt.EventManager.consumeEvent(evt);
    thisRef.CloseDialog();

    // Reset the stack preview states
    var propEvent = dvt.EventFactory.newAdfPropertyChangeEvent();
    propEvent['properties'][dvt.AdfDiagram.PREVIEW_ROW_KEY] = 'null';
    propEvent['properties'][dvt.AdfDiagram.PREVIEW_PAN_ZOOM_KEY] = 'null';
    thisRef._diagram.dispatchEvent(propEvent);
  };
  //Bug 13381888: clicking on close button shouldn't affect selection
  closeButtonHandler.isUnrelatedToSelection = function() {
    return true;
  };
  this._diagram.getEventManager().associate(closeButton, closeButtonHandler);
  return closeButton;
};

DvtAdfDiagramStackBadgeHandler.prototype.CreatePinButton = function()
{
  var iconEna = this._diagram.getResourcesMap()['stackPreviewUnstackEna'];
  var iconOvr = this._diagram.getResourcesMap()['stackPreviewUnstackOvr'];
  var iconDwn = this._diagram.getResourcesMap()['stackPreviewUnstackDwn'];
  var tooltip = this._diagram.getResourcesMap()['unstackTip'];
  var pinButton = this._diagram.CreateButton(iconEna, iconOvr, iconDwn, dvt.AdfDiagram._BUTTON_SIZE, dvt.AdfDiagram._BUTTON_SIZE + this._diagram._buttonBorderRadius, this._diagram._buttonBorderRadius);
  var pinButtonHandler = {};
  pinButtonHandler.getTooltip = function() {
    return tooltip;
  };
  var thisRef = this;
  pinButtonHandler.HandleClick = function(evt) {
    //Bug 13381888: consume the event
    dvt.EventManager.consumeEvent(evt);
    thisRef.CloseDialog(true);
  };
  //Bug 13381888: clicking on pin button shouldn't affect selection
  pinButtonHandler.isUnrelatedToSelection = function() {
    return true;
  };
  this._diagram.getEventManager().associate(pinButton, pinButtonHandler);
  return pinButton;
};

/**
 * Create diagram nodes for the passed node ids
 * @param {array} arNodeIds  array of node ids
 * @return {array}  array of dvt.AdfDiagramNode
 */
DvtAdfDiagramStackBadgeHandler.prototype.CreateNodes = function(arNodeIds)
{
  var numNodes = arNodeIds.length;
  var arNodes = [];
  var nodeId;
  var node;
  for (var i = 0; i < numNodes; i++)
  {
    nodeId = arNodeIds[i];
    node = this.CloneNode(this._diagram.getNodeById(nodeId));
    arNodes.push(node);
  }
  return arNodes;
};


/**
 * Render stacked nodes
 * @protected
 * @param {array} arNodes  Array of dvt.AdfDiagramNode
 */
DvtAdfDiagramStackBadgeHandler.prototype.RenderNodes = function(arNodes)
{
  var afContext = this._diagram.createAfContext();

  var i;
  var numNodes = arNodes.length;
  var node;

  for (i = 0; i < numNodes; i++)
  {
    node = arNodes[i];

    node.createNodeContents(afContext);
  }

  //NOTE: make sure the dom is not changing during this call.

  for (i = 0; i < numNodes; i++)
  {
    node = arNodes[i];
    node.renderNode(afContext);
  }

};

DvtAdfDiagramStackBadgeHandler.prototype.CloneNode = function(node)
{
  var newNode = new dvt.AdfDiagramNode(this._diagram.getCtx(), node.getData(), this._diagram);
  newNode.setForceZoom100Facet(true);
  newNode.setFlyOutEnabled(false);
  newNode.setNodeDef(node.getNodeDef());
  newNode.SetMaxAlpha(node.GetMaxAlpha());
  newNode.setAlpha(node.GetMaxAlpha());
  return newNode;
};

DvtAdfDiagramStackBadgeHandler.prototype.HandleZoom = function(event)
{
  switch (event.getSubType())
  {
    case dvt.ZoomEvent.SUBTYPE_ZOOMED:
      if (event.getOldZoom() !== event.getNewZoom())
      {
        var oldZoom = Math.min(event.getOldZoom(), 1);
        var newZoom = Math.min(event.getNewZoom(), 1);
        oldZoom = Math.ceil(oldZoom / .25);
        newZoom = Math.ceil(newZoom / .25);
        if (oldZoom !== newZoom)
        {
          this.RenderNodes(this._arNodes);
        }
      }
      break;
  }
};

DvtAdfDiagramStackBadgeHandler.prototype.isDialogVisible = function() {
  return this._isDialogVisible;
};
// Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.

/**
 * Dvt Adf Diagram Transition Utils class
 */
dvt.AdfDiagramTransitionUtils = {};

dvt.Obj.createSubclass(dvt.AdfDiagramTransitionUtils, dvt.Obj, 'dvt.AdfDiagramTransitionUtils');

// Filter -> ContainerCount -> Drill -> Isolate -> Stack -> ContainerDisclosure


/**
 * Processes diagram links and nodes using legend filter
 * @param {dvt.AdfDiagram} diagram The diagram component that has nodes and links to process
 * @param {boolean} bSetVisibilityOnly Flag to set nodes visibility with regard to the filter and skip the animation
 * @param {boolean} bSkipDataAnimPrep Flag to skip the step needed for animation (saving old diagram state, reseting nodes and links)
 */
dvt.AdfDiagramTransitionUtils.processFilter = function(diagram, bSetVisibilityOnly, bSkipDataAnimPrep) {
  //may need to reset nodes and links for the data anim handler
  if (!bSkipDataAnimPrep) {
    dvt.AdfDiagramTransitionUtils.__prepForDataAnim(diagram, true);
  }

  //Bug 16493667: can't loop over map because order may be different in Flash, so
  //loop over array instead
  var incomingNodes = dvt.AdfDiagramTransitionUtils._buildListFromKeys(diagram._arNodeIds);
  var incomingLinks = dvt.AdfDiagramTransitionUtils._buildListFromKeys(diagram._arLinkIds);

  var arHiddenAttrGroups = diagram.getHiddenAttributeGroups();
  var outgoingNodes = incomingNodes.slice(0);
  var filteredNodes = [];
  if (arHiddenAttrGroups) {
    for (var i = 0; i < incomingNodes.length; i++) {
      var nodeId = incomingNodes[i];
      var node = diagram.getNodeById(nodeId);
      var nodeAttrGroups = node.getData().getAttributeGroups();
      if (nodeAttrGroups) {
        for (var j = 0; j < nodeAttrGroups.length; j++) {
          if (dvt.ArrayUtils.getIndex(arHiddenAttrGroups, nodeAttrGroups[j]) != -1) {
            filteredNodes.push(nodeId);
            break;
          }
        }
      }
    }
  }

  dvt.AdfDiagramTransitionUtils._removeDescendants(outgoingNodes, filteredNodes, diagram);
  var outgoingLinks = dvt.AdfDiagramTransitionUtils._filterLinks(incomingLinks, outgoingNodes, diagram);

  diagram._nodesPostFilter = outgoingNodes;
  diagram._linksPostFilter = outgoingLinks;

  dvt.AdfDiagramTransitionUtils.processContainerCount(diagram, bSetVisibilityOnly, true);
};


/**
 * Processes diagram container nodes - sets the counter for visible children for each node
 * @param {dvt.AdfDiagram} diagram The diagram component that has nodes and links to process
 * @param {boolean} bSetVisibilityOnly Flag to set nodes visibility and skip the animation
 * @param {boolean} bSkipDataAnimPrep Flag to skip the step needed for animation (saving old diagram state, reseting nodes and links)
 */
dvt.AdfDiagramTransitionUtils.processContainerCount = function(diagram, bSetVisibilityOnly, bSkipDataAnimPrep) {
  //may need to reset nodes and links for the data anim handler
  if (!bSkipDataAnimPrep) {
    dvt.AdfDiagramTransitionUtils.__prepForDataAnim(diagram, true);
  }

  var incomingNodes = diagram._nodesPostFilter;
  var incomingLinks = diagram._linksPostFilter;

  if (!incomingNodes || !incomingLinks) {
    dvt.AdfDiagramTransitionUtils.processFilter(diagram, bSetVisibilityOnly, true);
    return;
  }

  for (var i = 0; i < incomingNodes.length; i++) {
    var nodeId = incomingNodes[i];
    var node = diagram.getNodeById(nodeId);
    if (node.getChildNodeCount() > 0) {
      var childNodeIds = node.getChildNodeIds();
      var visChildCount = 0;
      for (var j = 0; j < childNodeIds.length; j++) {
        var childNodeId = childNodeIds[j];
        if (dvt.ArrayUtils.getIndex(incomingNodes, childNodeId) != -1) {
          visChildCount++;
        }
      }
      if (visChildCount != node.getVisibleChildNodeCount()) {
        node.setVisibleChildNodeCount(visChildCount);
      }
    }
  }

  dvt.AdfDiagramTransitionUtils.processDrill(diagram, bSetVisibilityOnly, true);
};


/**
 * Processes diagram nodes and links for the drill operation
 * @param {dvt.AdfDiagram} diagram The diagram component that has nodes and links to process
 * @param {boolean} bSetVisibilityOnly Flag to set nodes visibility and skip the animation
 * @param {boolean} bSkipDataAnimPrep Flag to skip the step needed for animation (saving old diagram state, reseting nodes and links)
 */
dvt.AdfDiagramTransitionUtils.processDrill = function(diagram, bSetVisibilityOnly, bSkipDataAnimPrep) {
  //may need to reset nodes and links for the data anim handler
  if (!bSkipDataAnimPrep) {
    dvt.AdfDiagramTransitionUtils.__prepForDataAnim(diagram, true);
  }

  var incomingNodes = diagram._nodesPostFilter;
  var incomingLinks = diagram._linksPostFilter;

  if (!incomingNodes || !incomingLinks) {
    dvt.AdfDiagramTransitionUtils.processFilter(diagram, bSetVisibilityOnly, true);
    return;
  }

  var outgoingNodes;
  var outgoingLinks;
  if (diagram._drilledNodePath) {
    outgoingNodes = [];
    var drilledNodeId = diagram._drilledNodePath[diagram._drilledNodePath.length - 1];
    for (var i = 0; i < incomingNodes.length; i++) {
      var nodeId = incomingNodes[i];
      var node = diagram.getNodeById(nodeId);
      //only view children of last drilled container
      var groupId = node.getData().getGroupId();
      while (groupId) {
        if (groupId == drilledNodeId) {
          outgoingNodes.push(nodeId);
        }
        groupId = diagram.getNodeById(groupId).getData().getGroupId();
      }
    }

    for (var j = 0; j < diagram._drilledNodePath.length; j++) {
      var nodeId = diagram._drilledNodePath[j];
      var node = diagram.getNodeById(nodeId);
      outgoingNodes.push(nodeId);
    }

    outgoingLinks = dvt.AdfDiagramTransitionUtils._filterLinks(incomingLinks, outgoingNodes, diagram);
  }
  else {
    outgoingNodes = incomingNodes;
    outgoingLinks = incomingLinks;
  }

  diagram._nodesPostDrill = outgoingNodes;
  diagram._linksPostDrill = outgoingLinks;

  dvt.AdfDiagramTransitionUtils.processIsolate(diagram, bSetVisibilityOnly, true);
};


/**
 * Processes diagram nodes and links for the isolate operation
 * @param {dvt.AdfDiagram} diagram The diagram component that has nodes and links to process
 * @param {boolean} bSetVisibilityOnly Flag to set nodes visibility and skip the animation
 * @param {boolean} bSkipDataAnimPrep Flag to skip the step needed for animation (saving old diagram state, reseting nodes and links)
 */
dvt.AdfDiagramTransitionUtils.processIsolate = function(diagram, bSetVisibilityOnly, bSkipDataAnimPrep) {
  //may need to reset nodes and links for the data anim handler
  if (!bSkipDataAnimPrep) {
    dvt.AdfDiagramTransitionUtils.__prepForDataAnim(diagram, true);
  }

  var incomingNodes = diagram._nodesPostDrill;
  var incomingLinks = diagram._linksPostDrill;

  if (!incomingNodes || !incomingLinks) {
    dvt.AdfDiagramTransitionUtils.processDrill(diagram, bSetVisibilityOnly, true);
    return;
  }

  var isolatedNodesWithAncestors = {};
  var ancestorMap = {};
  if (diagram._isolatedNodes) {
    for (var i = 0; i < diagram._isolatedNodes.length; i++) {
      var nodeId = diagram._isolatedNodes[i];
      isolatedNodesWithAncestors[nodeId] = nodeId;
      var ancestors = dvt.AdfDiagramTransitionUtils._getAncestors(diagram, nodeId);
      ancestorMap[nodeId] = ancestors;
      for (var j = 0; j < ancestors.length; j++) {
        var ancestorId = ancestors[j];
        isolatedNodesWithAncestors[ancestorId] = ancestorId;
      }
    }
  }

  var outgoingNodes = [];
  var commonAncestors = null;
  for (var i = 0; i < incomingNodes.length; i++) {
    var nodeId = incomingNodes[i];
    if (!diagram._isolatedNodes || isolatedNodesWithAncestors[nodeId]) {
      outgoingNodes.push(nodeId);
      if (ancestorMap[nodeId]) {
        var ancestors = ancestorMap[nodeId];
        if (commonAncestors) {
          for (var j = 0; j < commonAncestors.length; j++) {
            if (j > ancestors.length || commonAncestors[j] != ancestors[j]) {
              // commonAncestors[j] and anything after it are not ancestors of nodeId, remove them from commonAncestors
              commonAncestors.splice(j, commonAncestors.length - j);
              break;
            }
          }
        }
        else {
          commonAncestors = ancestors;
        }
      }
    }
  }

  var removedLinks = [];
  var outgoingLinks = dvt.AdfDiagramTransitionUtils._filterLinks(incomingLinks, outgoingNodes, diagram, removedLinks);

  diagram._nodesPostIsolate = outgoingNodes;
  diagram._linksPostIsolate = outgoingLinks;
  diagram._linksRemovedByIsolate = removedLinks;
  diagram._hiddenAncestors = commonAncestors ? commonAncestors : diagram._drilledNodePath;
  //Bug 16472982: hidden ancestors can be union of both common
  //ancestors and drilled node path
  if (commonAncestors && diagram._drilledNodePath) {
    for (var i = 0; i < diagram._drilledNodePath.length; i++) {
      //if this node along the drilled node path is not already in the
      //hidden ancestors array, add it
      var index = dvt.ArrayUtils.getIndex(diagram._hiddenAncestors, diagram._drilledNodePath[i]);
      if (index < 0) {
        diagram._hiddenAncestors.push(diagram._drilledNodePath[i]);
      }
    }
  }

  dvt.AdfDiagramTransitionUtils.processStack(diagram, bSetVisibilityOnly, true);
};


/**
 * Gets ancestors for the specified node
 * @param {dvt.AdfDiagram} diagram the diagram component
 * @param {string} nodeId the node id
 * @return {array} array of ancestors for the specified node
 * @private
 */
dvt.AdfDiagramTransitionUtils._getAncestors = function(diagram, nodeId) {
  var ancestors = [];
  var curId = nodeId;
  while (curId) {
    var node = diagram.getNodeById(curId);
    curId = node ? node.getData().getGroupId() : null;
    if (curId) {
      ancestors.unshift(curId);
    }
  }
  return ancestors;
};


/**
 * Processes diagram nodes and links using attribute groups for stacking
 * @param {dvt.AdfDiagram} diagram The diagram component that has nodes and links to process
 * @param {boolean} bSetVisibilityOnly Flag to set nodes visibility and skip the animation
 * @param {boolean} bSkipDataAnimPrep Flag to skip the step needed for animation (saving old diagram state, reseting nodes and links)
 */
dvt.AdfDiagramTransitionUtils.processStack = function(diagram, bSetVisibilityOnly, bSkipDataAnimPrep) {
  //may need to reset nodes and links for the data anim handler
  if (!bSkipDataAnimPrep) {
    dvt.AdfDiagramTransitionUtils.__prepForDataAnim(diagram, true);
  }

  var incomingNodes = diagram._nodesPostIsolate;
  var incomingLinks = diagram._linksPostIsolate;

  if (!incomingNodes || !incomingLinks) {
    dvt.AdfDiagramTransitionUtils.processIsolate(diagram, bSetVisibilityOnly, true);
    return;
  }

  var stackMaps = dvt.AdfDiagramTransitionUtils._buildStackMaps(incomingNodes, incomingLinks, diagram._arAttrGrps, diagram._arExplodedStacks, diagram);
  var stackNodeMap = stackMaps[0];
  var nodeStackMap = stackMaps[1];
  var arUnstackedNodes = stackMaps[2];

  var oldStackMaps = dvt.AdfDiagramTransitionUtils._buildStackMaps(incomingNodes, incomingLinks, diagram._arOldAttrGrps, diagram._arOldExplodedStacks, diagram);
  var oldStackNodeMap = oldStackMaps[0];
  var oldNodeStackMap = oldStackMaps[1];
  var arOldUnstackedNodes = oldStackMaps[2];

  var outgoingNodes;
  var outgoingLinks;

  if (stackNodeMap) {
    var candidates = [];
    var stackName;
    for (var stackName in stackNodeMap) {
      candidates.push(stackNodeMap[stackName][0]);
    }
    if (arUnstackedNodes) {
      for (var ii = 0; ii < arUnstackedNodes.length; ii++) {
        candidates.push(arUnstackedNodes[ii]);
      }
    }
    outgoingNodes = [];
    for (var i = 0; i < candidates.length; i++) {
      var nodeId = candidates[i];
      var kept = true;
      while (nodeId) {
        var node = diagram.getNodeById(nodeId);
        nodeId = node.getData().getGroupId();
        if (nodeId) {
          kept = dvt.ArrayUtils.getIndex(candidates, nodeId) != -1;
          if (!kept) {
            break;
          }
        }
      }
      if (kept) {
        outgoingNodes.push(candidates[i]);
      }
    }

    outgoingLinks = [];
    var stackLinkPromotionMap = {};
    var stackPromotionLinkMap = {};
    var anythingPromoted = false;
    for (var i = 0; i < incomingLinks.length; i++) {
      var linkId = incomingLinks[i];
      var link = diagram.getLinkById(linkId);
      var parentId = link.getData().getStartId();
      var childId = link.getData().getEndId();
      if (dvt.ArrayUtils.getIndex(outgoingNodes, parentId) != -1 && dvt.ArrayUtils.getIndex(outgoingNodes, childId) != -1) {
        // not promoted
        outgoingLinks.push(linkId);
      }
      else {
        var promotedParentId = dvt.AdfDiagramTransitionUtils._findFirstVisibleNode(parentId, outgoingNodes, stackNodeMap, nodeStackMap, diagram);
        var promotedChildId = dvt.AdfDiagramTransitionUtils._findFirstVisibleNode(childId, outgoingNodes, stackNodeMap, nodeStackMap, diagram);
        if (promotedParentId == promotedChildId) {
          // Link is hidden within stack
          continue;
        }
        else {
          anythingPromoted = true;
          var promotion = [];
          promotion.push(promotedParentId);
          promotion.push(promotedChildId);
          var promotionId = promotedParentId + promotedChildId;
          stackLinkPromotionMap[linkId] = promotion;
          var promotedLinks = stackPromotionLinkMap[promotionId];
          if (!promotedLinks) {
            promotedLinks = [];
            stackPromotionLinkMap[promotionId] = promotedLinks;
            // First link in stack gets kept
            outgoingLinks.push(linkId);
          }
          promotedLinks.push(linkId);
        }
      }
    }
  }
  else {
    outgoingNodes = incomingNodes.slice(0);
    outgoingLinks = incomingLinks.slice(0);
  }

  diagram._nodesPostStack = outgoingNodes;
  diagram._linksPostStack = outgoingLinks;
  diagram._stackNodeMap = stackNodeMap;
  diagram._nodeStackMap = nodeStackMap;
  diagram._oldStackNodeMap = oldStackNodeMap;
  diagram._oldNodeStackMap = oldNodeStackMap;
  if (!anythingPromoted) {
    stackLinkPromotionMap = null;
    stackPromotionLinkMap = null;
  }
  diagram._oldStackLinkPromotionMap = diagram._stackLinkPromotionMap;
  diagram._oldStackPromotionLinkMap = diagram._stackPromotionLinkMap;
  diagram._stackLinkPromotionMap = stackLinkPromotionMap;
  diagram._stackPromotionLinkMap = stackPromotionLinkMap;

  dvt.AdfDiagramTransitionUtils.processContainerDisclosure(diagram, bSetVisibilityOnly, true);
};


/**
 * Processes diagram nodes and links for container disclosure
 * @param {dvt.AdfDiagram} diagram The diagram component that has nodes and links to process
 * @param {boolean} bSetVisibilityOnly Flag to set nodes visibility and skip the animation
 * @param {boolean} bSkipDataAnimPrep Flag to skip the step needed for animation (saving old diagram state, reseting nodes and links)
 */
dvt.AdfDiagramTransitionUtils.processContainerDisclosure = function(diagram, bSetVisibilityOnly, bSkipDataAnimPrep) {
  //may need to reset nodes and links for the data anim handler
  if (!bSkipDataAnimPrep) {
    dvt.AdfDiagramTransitionUtils.__prepForDataAnim(diagram, true);
  }

  var incomingNodes = diagram._nodesPostStack;
  var incomingLinks = diagram._linksPostStack;

  if (!incomingNodes || !incomingLinks) {
    dvt.AdfDiagramTransitionUtils.processStack(diagram, bSetVisibilityOnly, true);
    return;
  }

  var outgoingNodes = [];
  var outgoingLinks = [];
  var disclosedNodes = [];
  var containerLinkPromotionMap = {};
  var containerPromotionLinkMap = {};

  var stackNodeMap = diagram._stackNodeMap;
  var nodeStackMap = diagram._nodeStackMap;

  for (var i = 0; i < incomingNodes.length; i++) {
    var nodeId = incomingNodes[i];
    var node = diagram.getNodeById(nodeId);
    if (dvt.AdfDiagramTransitionUtils.IsNodeDisclosed(diagram, nodeId) && (!nodeStackMap || !nodeStackMap[nodeId])) {
      disclosedNodes.push(nodeId);
      node.setDisclosed(true);
    }
    else {
      node.setDisclosed(false);
    }
  }

  for (var i = 0; i < incomingNodes.length; i++) {
    var nodeId = incomingNodes[i];
    var node = diagram.getNodeById(nodeId);
    var groupId = node.getData().getGroupId();
    var groupNode;
    var bDisclosed = true;
    while (groupId) {
      groupNode = diagram.getNodeById(groupId);
      if (groupNode) {
        if (!groupNode.isDisclosed()) {
          bDisclosed = false;
          break;
        }
        groupId = groupNode.getData().getGroupId();
      }
    }
    if (bDisclosed) {
      outgoingNodes.push(nodeId);
    }
  }

  var outgoingNodeMap = dvt.AdfDiagramTransitionUtils._makeMap(outgoingNodes);
  for (var j = 0; j < incomingLinks.length; j++) {
    var linkId = incomingLinks[j];
    var link = diagram.getLinkById(linkId);
    var groupId = link.getData().getGroupId();
    var bDisclosed = true;
    while (groupId) {
      var groupNode = diagram.getNodeById(groupId);
      if (groupNode) {
        if (!groupNode.isDisclosed()) {
          bDisclosed = false;
          break;
        }
        groupId = groupNode.getData().getGroupId();
      }
    }
    if (bDisclosed) {
      // Link is represented in the diagram, now need to figure out whether it's promoted or not
      var pNodeId = link.getData().getStartId();
      var cNodeId = link.getData().getEndId();
      if (outgoingNodeMap[pNodeId] && outgoingNodeMap[cNodeId] &&
          (!nodeStackMap || (!nodeStackMap[pNodeId] && !nodeStackMap[cNodeId]))) {
        // Direct (non-promoted) link
        outgoingLinks.push(linkId);
      }
      else {
        // Promoted link
        var pPromotedId = dvt.AdfDiagramTransitionUtils._findFirstVisibleNode(pNodeId, outgoingNodes, diagram._stackNodeMap, diagram._nodeStackMap, diagram);
        var cPromotedId = dvt.AdfDiagramTransitionUtils._findFirstVisibleNode(cNodeId, outgoingNodes, diagram._stackNodeMap, diagram._nodeStackMap, diagram);
        if (pPromotedId === cPromotedId) {
          continue;
        }
        var promotion = [];
        promotion.push(pPromotedId);
        promotion.push(cPromotedId);
        var promotionId = pPromotedId + cPromotedId;
        containerLinkPromotionMap[linkId] = promotion;
        var linkPromotion = containerPromotionLinkMap[promotionId];
        if (!linkPromotion) {
          linkPromotion = [];
          containerPromotionLinkMap[promotionId] = linkPromotion;
          // First promoted link gets kept
          outgoingLinks.push(linkId);
        }
        var added = false;
        // Check to see if this link already represents a promotion
        if (diagram._stackLinkPromotionMap && diagram._stackPromotionLinkMap) {
          var promotion = diagram._stackLinkPromotionMap[linkId];
          if (promotion) {
            added = true;
            var promotionId = promotion[0] + promotion[1];
            var promotedLinks = diagram._stackPromotionLinkMap[promotionId];
            for (var k = 0; k < promotedLinks.length; k++) {
              linkPromotion.push(promotedLinks[k]);
            }
          }
        }
        if (!added) {
          linkPromotion.push(linkId);
        }
      }
    }
  }

  //Bug 14851707:
  //NOTE: the final result of the last phase of the
  //dvt.AdfDiagramTransitionUtils processing sequence is accessible through the
  //dvt.AdfDiagram.GetLayoutNodeIdsList() function, so that function must be
  //kept in sync with this file (currently returns
  //_nodesPostContainerDisclosure)
  diagram._nodesPostContainerDisclosure = outgoingNodes;
  diagram._linksPostContainerDisclosure = outgoingLinks;
  diagram._oldContainerLinkPromotionMap = diagram._containerLinkPromotionMap;
  diagram._oldContainerPromotionLinkMap = diagram._containerPromotionLinkMap;
  diagram._containerLinkPromotionMap = containerLinkPromotionMap;
  diagram._containerPromotionLinkMap = containerPromotionLinkMap;
  diagram._disclosedNodesPostContainerDisclosure = disclosedNodes;

  if (bSetVisibilityOnly) {
    dvt.AdfDiagramTransitionUtils._setVisibility(diagram);
  }
  else {
    dvt.AdfDiagramTransitionUtils.__animateTransition(diagram, false);
  }
};

dvt.AdfDiagramTransitionUtils.setVisibility = function(diagram) {
  //call the last phase in the sequence, which will call earlier phases as needed
  dvt.AdfDiagramTransitionUtils.processContainerDisclosure(diagram, true, true);
};

dvt.AdfDiagramTransitionUtils._buildStackMaps = function(nodeList, linkList, attrGroups, arExplodedStacks, diagram) {
  var stackNodeMap;
  var nodeStackMap;
  var arUnstackedNodes;
  if (attrGroups && attrGroups.length > 0) {
    var initialStackNodeMap = {};
    arUnstackedNodes = [];

    for (var i = 0; i < nodeList.length; i++) {
      var nodeId = nodeList[i];
      var stackName = dvt.AdfDiagramTransitionUtils._getStackName(nodeId, attrGroups, diagram);

      if (stackName) {
        if (arExplodedStacks &&
            arExplodedStacks.length > 0 &&
            dvt.ArrayUtils.getIndex(arExplodedStacks, stackName) > -1)
        {
          arUnstackedNodes.push(nodeId);
          continue;
        }

        if (initialStackNodeMap[stackName]) {
          initialStackNodeMap[stackName].push(nodeId);
        }
        else {
          initialStackNodeMap[stackName] = [nodeId];
        }
      }
      else {
        arUnstackedNodes.push(nodeId);
      }
    }
    stackNodeMap = {};
    nodeStackMap = {};
    for (var stackName in initialStackNodeMap) {
      var stackNodes = initialStackNodeMap[stackName];
      if (stackNodes.length == 1) {
        // Move single stacks to unstackedNodes
        arUnstackedNodes.push(stackNodes[0]);
      }
      else {
        stackNodeMap[stackName] = stackNodes;
        for (var i = 0; i < stackNodes.length; i++) {
          nodeStackMap[stackNodes[i]] = stackName;
        }
      }
    }
  }

  var stackMaps = [];
  stackMaps.push(stackNodeMap);
  stackMaps.push(nodeStackMap);
  stackMaps.push(arUnstackedNodes);
  return stackMaps;
};

dvt.AdfDiagramTransitionUtils._getStackName = function(nodeId, attrGroups, diagram) {
  return diagram.CreateStackName(nodeId, attrGroups);
};


/**
 * Animates nodes and links transition from old state to new
 * @param {dvt.AdfDiagram} diagram The diagram component that has nodes and links to process
 * @param {boolean} bSkipRendering Flag to skip component rendering - not necessary in case of the new xml
 */
dvt.AdfDiagramTransitionUtils.__animateTransition = function(diagram, bSkipRendering) {
  dvt.AdfDiagramTransitionUtils._animateTransitionDataHandler(diagram, bSkipRendering);
};


/**
 * Prepares data for animation - saves old diagram state, hides action toolbar, resets nodes and links
 * @param {dvt.AdfDiagram} diagram The diagram component to process
 * @param {boolean} bRepopulateNodesAndLinks Whether to copy old nodes and links to the new nodes and links structures
 */
dvt.AdfDiagramTransitionUtils.__prepForDataAnim = function(diagram, bRepopulateNodesAndLinks) {
  //save old diagram state before processing any phases
  diagram._oldDataAnimState = diagram.CreateDataAnimationState();
  //need to hide the transient toolbar before resetting nodes and links,
  //because afterwards it becomes orphaned
  diagram.HideNodeTransientToolbar();
  //create new nodes and links
  diagram.ResetNodesAndLinks(bRepopulateNodesAndLinks);
};


/**
 * @private
 * Animates nodes and links transition from old state to new
 * @param {dvt.AdfDiagram} diagram The diagram component that has nodes and links to process
 * @param {boolean} bSkipRendering Flag to skip component rendering - not necessary in case of the new xml
 */
dvt.AdfDiagramTransitionUtils._animateTransitionDataHandler = function(diagram, bSkipRendering) {

  diagram.setInteractionEnabled(false);
  diagram._processHighlighting();
  dvt.AdfDiagramTransitionUtils._setVisibility(diagram);
  //trigger creation of new overview content
  diagram._refreshOverviewContent = true;
  if (!bSkipRendering)
    diagram.RenderComponentInternal();

  //re-apply focus
  diagram.UpdateKeyboardFocus();
  //re-apply selection
  if (diagram._selectionHandler) {
    var selectedIds = diagram._selectionHandler.getSelectedIds();
    if (selectedIds && selectedIds.length > 0) {
      diagram._selectedNodes = selectedIds;
      diagram._processInitialSelections();
    }
  }
  //diagram.getPanZoomCanvas().zoomToFit(null, diagram.GetViewBounds());
  var zoomFunc = dvt.AdfDiagramTransitionUtils._createZoomFunc(diagram, true);
  zoomFunc();

  var dataAnimHandler = new DvtAdfDiagramDataAnimationHandler(diagram.getCtx());
  dataAnimHandler.ConstructAnimation(diagram._oldDataAnimState, diagram, diagram._oldNodes, diagram._nodes, diagram._oldLinks, diagram._links,
                                     diagram._oldArNodeIds, diagram._arNodeIds, diagram._oldArLinkIds, diagram._arLinkIds);
  diagram._oldDataAnimState = null;
  var playable = dataAnimHandler.getAnimation();

  //remove the old nodes and links panes after constructing the animation
  //so they're not on the dom while animating
  diagram._oldLinksPane.getParent().removeChild(diagram._oldLinksPane);
  diagram._oldNodesPane.getParent().removeChild(diagram._oldNodesPane);
  diagram._oldTopPane.getParent().removeChild(diagram._oldTopPane);

  var tooltipsEnabled = diagram.getEventManager().getTooltipsEnabled();
  diagram.getEventManager().setTooltipsEnabled(false);
  var tooltipManager = diagram.getCtx().getTooltipManager();
  if (tooltipManager) {
    tooltipManager.hideTooltip();
  }

  if (playable) {
    var enableTooltipsFunc = function() {
      diagram.getEventManager().setTooltipsEnabled(tooltipsEnabled);
    };
    var enableMouseFunc = function() {
      diagram.setInteractionEnabled(true);
    };
    var clearAutoStatesFunc = function() {
      diagram.clearAutomationState();
    };
    dvt.Playable.appendOnEnd(playable, enableTooltipsFunc);
    dvt.Playable.appendOnEnd(playable, enableMouseFunc);
    dvt.Playable.appendOnEnd(playable, clearAutoStatesFunc);
    dvt.Playable.appendOnEnd(playable, diagram.destroyOldContent, diagram);
    diagram.setAnimation(playable);
    playable.play();
  }
  else {
    diagram.setInteractionEnabled(true);
    diagram.clearAutomationState();
  }
};

/**
 * @private
 * create zoom function
 * @param {dvt.AdfDiagram} diagram  diagram object
 * @param {boolean} bDoNotCreateAnimator flag indicates whether to create animation or not
 * @return {function}  returns zoom function
 */
dvt.AdfDiagramTransitionUtils._createZoomFunc = function(diagram, bDoNotCreateAnimator) {
  var func = function(animator) {
    //BUG FIX #13393132: if opening a container, zoom to fit that container;
    //if closing a container, pan the node into view, or zoom to fit the
    //whole diagram if we're already zoomed out farther than the
    //zoom-to-fit would go
    //BUG FIX #13770228: if opening a container, zoom-to-fit the container
    //if it's bigger than the viewport; otherwise, just pan to move the
    //container into the viewport
    var fitBounds = null;
    //BUG FIX #13770228: if we don't need to adjust pan or zoom, then
    //basically skip this animation step
    var bSkipZoom = false;
    if (diagram._disclosingNode) {
      var nodeBounds = diagram.GetViewBounds(animator, [diagram._disclosingNode], []);
      var viewportBounds = diagram.getPanZoomCanvas().getViewport();
      //need to scale the zoom-to-fit padding by the inverse of the zoom
      var ztfPadding = diagram.getPanZoomCanvas().getZoomToFitPadding() /
                       diagram.getPanZoomCanvas().getZoom();
      //decrease the viewportBounds by the zoom-to-fit padding, because the
      //zoom-to-fit will automatically re-add the padding
      viewportBounds.x += ztfPadding;
      viewportBounds.y += ztfPadding;
      viewportBounds.w -= 2 * ztfPadding;
      viewportBounds.h -= 2 * ztfPadding;

      var bNodeFits = (nodeBounds.w <= viewportBounds.w &&
                       nodeBounds.h <= viewportBounds.h);
      //BUG FIX #13770228: if the node is bigger than the viewport,
      //zoom-to-fit it
      if (!bNodeFits) {
        fitBounds = nodeBounds;
      }
      //BUG FIX #13770228: if the node already fits within the viewport,
      //then just pan it entirely into view
      else {
        var panOffset = diagram.CalcOffsetToPanNodeIntoView(diagram._disclosingNode, animator);
        if (panOffset && (panOffset.x != 0 || panOffset.y != 0)) {
          fitBounds = viewportBounds;

          fitBounds.x -= panOffset.x;
          fitBounds.y -= panOffset.y;
        }
        //BUG FIX #13770228: if we don't need to adjust pan or zoom, then
        //basically skip this animation step
        else {
          bSkipZoom = true;
        }
      }
      //clear the saved node id
      diagram._disclosingNode = null;
    }
    else if (diagram._undisclosingNode) {
      //calc ztfScale based on node positions in animator
      var ztfBounds = diagram.GetViewBounds(animator);
      var ztfScale = diagram.getPanZoomCanvas().calcZoomToFitScale(ztfBounds);
      if (ztfScale < diagram.getPanZoomCanvas().getZoom()) {
        //var keepInPlaceOffset = diagram.CalcOffsetToKeepNodeInPlace(diagram._undisclosingNode, animator);
        //if (!keepInPlaceOffset) {
        //  keepInPlaceOffset = new dvt.Point(0, 0);
        //}
        var moveIntoViewOffset = diagram.CalcOffsetToPanNodeIntoView(diagram._undisclosingNode, animator);
        if (!moveIntoViewOffset) {
          moveIntoViewOffset = new dvt.Point(0, 0);
        }
        var panOffset = moveIntoViewOffset;
        if (panOffset && (panOffset.x != 0 || panOffset.y != 0)) {
          fitBounds = diagram.getPanZoomCanvas().getViewport();
          //need to scale the zoom-to-fit padding by the inverse of the zoom
          var ztfPadding = diagram.getPanZoomCanvas().getZoomToFitPadding() /
                           diagram.getPanZoomCanvas().getZoom();
          //decrease the fitBounds by the zoom-to-fit padding, because the
          //zoom-to-fit will automatically re-add the padding
          fitBounds.x += ztfPadding;
          fitBounds.y += ztfPadding;
          fitBounds.w -= 2 * ztfPadding;
          fitBounds.h -= 2 * ztfPadding;

          fitBounds.x -= panOffset.x;
          fitBounds.y -= panOffset.y;
        }
        //BUG FIX #13770228: if we don't need to adjust pan or zoom, then
        //basically skip this animation step
        else {
          bSkipZoom = true;
        }
      }
      //clear the saved node id
      diagram._undisclosingNode = null;
    }

    var minZoomFitBounds = diagram.AdjustMinZoom(animator);
    if (minZoomFitBounds && !fitBounds) {
      fitBounds = minZoomFitBounds;
    }

    //BUG FIX #13770228: if we don't need to adjust pan or zoom, then
    //basically skip this animation step by making the duration of the
    //animation tiny
    if (bSkipZoom) {
      //BUG FIX #13376397: run zoomFunc at same time as layoutFunc, so
      //don't decrease the animation duration of this step
    //      animator.setDuration(.001);
    }
    else {
      if (!fitBounds) {
        fitBounds = diagram.GetViewBounds(animator);
      }
      diagram.SyncOverviewAndZoomToFit(animator, fitBounds, bDoNotCreateAnimator);
    }
  };
  return func;
};

/**
 * @private
 * Filter links
 * @param {array} unfilteredLinks array of unfiltered link ids
 * @param {array} filteredNodes array of filtered node ids
 * @param {dvt.AdfDiagram} diagram diagram object
 * @param {array} removedLinks array of removed link ids
 * @return {array} array of filtered link ids
 */
dvt.AdfDiagramTransitionUtils._filterLinks = function(unfilteredLinks, filteredNodes, diagram, removedLinks) {
  var filteredNodeMap = dvt.AdfDiagramTransitionUtils._makeMap(filteredNodes);
  var filteredLinks = [];
  for (var i = 0; i < unfilteredLinks.length; i++) {
    var linkId = unfilteredLinks[i];
    var link = diagram.getLinkById(linkId);
    var parentId = link.getData().getStartId();
    var childId = link.getData().getEndId();
    if (filteredNodeMap[parentId] && filteredNodeMap[childId]) {
      filteredLinks.push(linkId);
    }
    else {
      if (removedLinks) {
        removedLinks.push(linkId);
      }
    }
  }
  return filteredLinks;
};

dvt.AdfDiagramTransitionUtils._calculateMatrix = function(oldA, oldB, newA, newB) {
  var distOld = Math.sqrt((oldA.x - oldB.x) * (oldA.x - oldB.x) + (oldA.y - oldB.y) * (oldA.y - oldB.y));
  var distNew = Math.sqrt((newA.x - newB.x) * (newA.x - newB.x) + (newA.y - newB.y) * (newA.y - newB.y));
  var s_cosT;
  var s_sinT;
  if (distNew == 0) {
    // scale = 0
    s_cosT = 0;
    s_sinT = 0;
  }
  else {
    var cosOld = (oldB.x - oldA.x) / distOld;
    var sinOld = (oldB.y - oldA.y) / distOld;
    var cosNew = (newB.x - newA.x) / distNew;
    var sinNew = (newB.y - newA.y) / distNew;
    // cos(rotation angle) = cos(new angle - old angle) = cosNew*cosOld + sinNew*sinOld
    // sin(rotation angle) = sin(new angle - old angle) = sinNew*cosOld - cosNew*sinOld
    var scale = distNew / distOld;
    var cosTransform = cosNew * cosOld + sinNew * sinOld;
    var sinTransform = sinNew * cosOld - cosNew * sinOld;

    s_cosT = scale * cosTransform;
    s_sinT = scale * sinTransform;
  }

  var a = s_cosT;
  var b = -s_sinT;
  var c = s_sinT;
  var d = s_cosT;
  var tx = -s_cosT * oldA.x + s_sinT * oldA.y + newA.x;
  var ty = -s_sinT * oldA.x - s_cosT * oldA.y + newA.y;

  var matrix = new dvt.Matrix();
  matrix._a = a;
  matrix._b = b;
  matrix._c = c;
  matrix._d = d;
  matrix._tx = tx;
  matrix._ty = ty;
  return matrix;
};

dvt.AdfDiagramTransitionUtils._buildListFromKeys = function(array) {
  var keyList = [];
  for (var index in array) {
    keyList.push(array[index]);
  }
  return keyList;
};

dvt.AdfDiagramTransitionUtils._makeMap = function(list) {
  if (!list) {
    return null;
  }
  var map = {};
  for (var i = 0; i < list.length; i++) {
    map[list[i]] = true;
  }
  return map;
};

/**
 * @private
 * Set visibility of nodes and links in diagram
 * @param {dvt.AdfDiagram} diagram  diagram object
 */
dvt.AdfDiagramTransitionUtils._setVisibility = function(diagram) {
  // TODO: should get rid of the lists entirely and replace with maps
  var nodeList = diagram._nodesPostContainerDisclosure;
  var nodeMap = dvt.AdfDiagramTransitionUtils._makeMap(nodeList);
  var linkList = diagram._linksPostContainerDisclosure;
  var linkMap = dvt.AdfDiagramTransitionUtils._makeMap(linkList);
  var stackNodeMap = diagram._stackNodeMap;
  var nodeStackMap = diagram._nodeStackMap;
  var promotionLinkMap = diagram._containerPromotionLinkMap;
  var linkPromotionMap = diagram._containerLinkPromotionMap;
  var hiddenAncestors = diagram._hiddenAncestors;
  var hiddenAncestorMap = dvt.AdfDiagramTransitionUtils._makeMap(hiddenAncestors);

  for (var i = 0; i < nodeList.length; i++) {
    var nodeId = nodeList[i];
    var node = diagram.getNodeById(nodeId);
    if (stackNodeMap && nodeStackMap) {
      var stackName = nodeStackMap[nodeId];
      if (stackName) {
        var stackNodes = stackNodeMap[stackName];
        if (nodeId === stackNodes[0] && stackNodes.length > 1) {
          node.setStackCount(stackNodes.length);
          node.setStack(true);
          node.addAccessibilityAttributes();
          continue;
        }
      }
    }
    node.setStack(false);
    node.setStackCount(0);
  }

  var partiallyExpandedNodes = {};
  for (var i = 0; i < diagram._linksRemovedByIsolate.length; i++) {
    var linkId = diagram._linksRemovedByIsolate[i];
    var link = diagram.getLinkById(linkId);
    var parentId = link.getData().getStartId();
    var childId = link.getData().getEndId();
    partiallyExpandedNodes[parentId] = parentId;
    partiallyExpandedNodes[childId] = childId;
  }
  for (var i = 0; i < nodeList.length; i++) {
    var nodeId = nodeList[i];
    var node = diagram.getNodeById(nodeId);
    if (node._bStack && diagram._nodeStackMap) {
      var stackName = diagram._nodeStackMap[nodeId];
      var stackNodes = diagram._stackNodeMap[stackName];
      var partiallyExpanded = false;
      for (var j = 0; j < stackNodes.length; j++) {
        if (partiallyExpandedNodes[stackNodes[j]]) {
          partiallyExpanded = true;
          break;
        }
      }
      node.setPartiallyExpanded(partiallyExpanded);
    }
    else {
      if (partiallyExpandedNodes[nodeId]) {
        node.setPartiallyExpanded(true);
      }
      else {
        node.setPartiallyExpanded(false);
      }
    }
  }

  for (var nodeId in diagram._nodes) {
    var node = diagram.getNodeById(nodeId);
    if (!nodeMap[nodeId] && node.getVisible()) {
      node.setVisible(false);
      node.setStack(false);
      node.setStackCount(0);
    }
    else if (nodeMap[nodeId] && !node.getVisible()) {
      node.setVisible(true);
    }

    if (hiddenAncestorMap && hiddenAncestorMap[nodeId]) {
      if (!node.isHiddenAncestor()) {
        node.setHiddenAncestor(true);
      }
    }
    else {
      if (node.isHiddenAncestor()) {
        node.setHiddenAncestor(false);
      }
    }
  }
  for (var i = 0; i < linkList.length; i++) {
    var linkId = linkList[i];
    var link = diagram.getLinkById(linkId);
    if (promotionLinkMap && linkPromotionMap) {
      var promotion = linkPromotionMap[linkId];
      if (promotion) {
        var promotionId = promotion[0] + promotion[1];
        var promotedLinks = promotionLinkMap[promotionId];
        if (linkId === promotedLinks[0]) {
          link.setPromotionCount(promotedLinks.length);
          link.setPromoted(true);
          continue;
        }
      }
    }
    link.setPromoted(false);
    link.setPromotionCount(0);
  }
  for (var linkId in diagram._links) {
    var link = diagram.getLinkById(linkId);
    link.setAlpha(Math.min(link.getAlpha(), link.GetMaxAlpha()));
    if (link._labelObj) {
      link._labelObj.setAlpha(Math.min(link._labelObj.getAlpha(), link.GetMaxAlpha()));
    }
    if (!linkMap[linkId] && link.getVisible()) {
      link.setVisible(false);
      link.setPromoted(false);
      link.setPromotionCount(0);
    }
    else if (linkMap[linkId] && !link.getVisible()) {
      link.setVisible(true);
    }
  }
};

dvt.AdfDiagramTransitionUtils.transformLinkPoints = function(originalLinkPoints,
    originalParentNodePos,
    originalParentNodeDimensions, 
    originalChildNodePos,
    originalChildNodeDimensions, 
    transformedParentNodePos,
    transformedParentNodeDimensions, 
    transformedChildNodePos,
    transformedChildNodeDimensions, 
    pointsToTransform) {
  if (!pointsToTransform) {
    pointsToTransform = originalLinkPoints;
  }
  var bOriginalPath = false;
  if (originalLinkPoints && originalLinkPoints.length > 0 && isNaN(originalLinkPoints[0]))
  {
    bOriginalPath = true;
  }

  var bTransformPath = false;
  if (pointsToTransform && pointsToTransform.length > 0 && isNaN(pointsToTransform[0]))
  {
    bTransformPath = true;
  }


  var originalParentLinkPos = bOriginalPath ?
      new dvt.Point(originalLinkPoints[1], originalLinkPoints[2]) :
      new dvt.Point(originalLinkPoints[0], originalLinkPoints[1]);
  var originalChildLinkPos = new dvt.Point(originalLinkPoints[originalLinkPoints.length - 2], originalLinkPoints[originalLinkPoints.length - 1]);

  var transformedParentLinkPos = new dvt.Point(transformedParentNodePos.x + transformedParentNodeDimensions.x + (originalParentLinkPos.x - originalParentNodePos.x - originalParentNodeDimensions.x) * transformedParentNodeDimensions.w / originalParentNodeDimensions.w,
      transformedParentNodePos.y + transformedParentNodeDimensions.y + (originalParentLinkPos.y - originalParentNodePos.y - originalParentNodeDimensions.y) * transformedParentNodeDimensions.h / originalParentNodeDimensions.h);
  var transformedChildLinkPos = new dvt.Point(transformedChildNodePos.x + transformedChildNodeDimensions.x + (originalChildLinkPos.x - originalChildNodePos.x - originalChildNodeDimensions.x) * transformedChildNodeDimensions.w / originalChildNodeDimensions.w,
      transformedChildNodePos.y + transformedChildNodeDimensions.y + (originalChildLinkPos.y - originalChildNodePos.y - originalChildNodeDimensions.y) * transformedChildNodeDimensions.h / originalChildNodeDimensions.h);

  var matrix = dvt.AdfDiagramTransitionUtils._calculateMatrix(originalParentLinkPos, originalChildLinkPos, transformedParentLinkPos, transformedChildLinkPos);

  var transformedPoints = [];
  var i;
  var transformedPoint;
  if (bTransformPath)
  {
    for (i = 0; i < pointsToTransform.length; i++) {
      var cmd = pointsToTransform[i];
      // TODO: more robust path handling, look at dvt.PathUtils.transformPath for starting point
      switch (cmd)
      {
        case 'M':
          transformedPoint = matrix.transformPoint(new dvt.Point(pointsToTransform[++i], pointsToTransform[++i]));
          transformedPoints.push(cmd, transformedPoint.x, transformedPoint.y);
          break;
        case 'L':
          transformedPoint = matrix.transformPoint(new dvt.Point(pointsToTransform[++i], pointsToTransform[++i]));
          transformedPoints.push(cmd, transformedPoint.x, transformedPoint.y);
          break;
        case 'Q':
          transformedPoint = matrix.transformPoint(new dvt.Point(pointsToTransform[++i], pointsToTransform[++i]));
          transformedPoints.push(cmd, transformedPoint.x, transformedPoint.y);
          transformedPoint = matrix.transformPoint(new dvt.Point(pointsToTransform[++i], pointsToTransform[++i]));
          transformedPoints.push(transformedPoint.x, transformedPoint.y);
          break;
        case 'C':
          transformedPoint = matrix.transformPoint(new dvt.Point(pointsToTransform[++i], pointsToTransform[++i]));
          transformedPoints.push(cmd, transformedPoint.x, transformedPoint.y);
          transformedPoint = matrix.transformPoint(new dvt.Point(pointsToTransform[++i], pointsToTransform[++i]));
          transformedPoints.push(transformedPoint.x, transformedPoint.y);
          transformedPoint = matrix.transformPoint(new dvt.Point(pointsToTransform[++i], pointsToTransform[++i]));
          transformedPoints.push(transformedPoint.x, transformedPoint.y);
          break;
      }
    }
  }
  else
  {
    for (i = 0; i < pointsToTransform.length; i += 2) {
      transformedPoint = matrix.transformPoint(new dvt.Point(pointsToTransform[i], pointsToTransform[i + 1]));
      transformedPoints.push(transformedPoint.x);
      transformedPoints.push(transformedPoint.y);
    }
  }
  return transformedPoints;
};

/**
 * @private
 * Remove descendants from the nodes
 * @param {array} nodes  array of node ids
 * @param {array} nodesToRemove  array of node ids to remove
 * @param {dvt.AdfDiagram} diagram  diagram object
 */
dvt.AdfDiagramTransitionUtils._removeDescendants = function(nodes, nodesToRemove, diagram) {
  for (var i = 0; i < nodesToRemove.length; i++) {
    var nodeId = nodesToRemove[i];
    var index = dvt.ArrayUtils.getIndex(nodes, nodeId);
    if (index != -1) {
      nodes.splice(index, 1);
    }
    var node = diagram.getNodeById(nodeId);
    if (node.getChildNodeCount() > 0) {
      dvt.AdfDiagramTransitionUtils._removeDescendants(nodes, node.getChildNodeIds(), diagram);
    }
  }
};

/**
 * @private
 * Get first visible node in stack
 * @param {string} nodeId  node id
 * @param {array} nodes  array of node ids to process
 * @param {object} stackNodeMap map of stack and nodes
 * @param {object} nodeStackMap map of node and stack
 * @param {dvt.AdfDiagram} diagram  diagram object
 * @return {string}  first visible node id
 */
dvt.AdfDiagramTransitionUtils._findFirstVisibleNode = function(nodeId, nodes, stackNodeMap, nodeStackMap, diagram) {
  if (dvt.ArrayUtils.getIndex(nodes, nodeId) != -1) {
    return nodeId;
  }
  if (stackNodeMap && nodeStackMap) {
    var stackName = nodeStackMap[nodeId];
    if (stackName) {
      var stackTop = stackNodeMap[stackName][0];
      if (nodeId != stackTop) {
        return dvt.AdfDiagramTransitionUtils._findFirstVisibleNode(stackTop, nodes, stackNodeMap, nodeStackMap, diagram);
      }
    }
  }
  var node = diagram.getNodeById(nodeId);
  var groupId = node.getData().getGroupId();
  if (groupId) {
    return dvt.AdfDiagramTransitionUtils._findFirstVisibleNode(groupId, nodes, stackNodeMap, nodeStackMap, diagram);
  }
  return null;
};

dvt.AdfDiagramTransitionUtils.IsNodeDisclosed = function(diagram, nodeId) {
  return ((diagram._disclosedNodes && dvt.ArrayUtils.getIndex(diagram._disclosedNodes, nodeId) > -1) ||
          (diagram._drilledNodePath && dvt.ArrayUtils.getIndex(diagram._drilledNodePath, nodeId) > -1));
};
var DvtAdfDiagramClientBehaviorHandler = function(context, callback, callbackObj) {
  this.Init(context, callback, callbackObj);
  this._diagram = callbackObj;
  this._suspendAnimation = false;
};

dvt.Obj.createSubclass(DvtAdfDiagramClientBehaviorHandler, dvt.ClientBehaviorHandler, 'DvtAdfDiagramClientBehaviorHandler');

DvtAdfDiagramClientBehaviorHandler.prototype.Init = function(context, callback, callbackObj) {
  DvtAdfDiagramClientBehaviorHandler.superclass.Init.call(this, context, callback, callbackObj);
};

DvtAdfDiagramClientBehaviorHandler.prototype.processClick = function(target, behaviors) {
  var consumed = DvtAdfDiagramClientBehaviorHandler.superclass.processClick.call(this, target, behaviors);
  if (consumed && !this._suspendAnimation) {
    // Need to trigger the animation because it was suppressed in case there were multiple clien behaviors
    dvt.AdfDiagramTransitionUtils.processFilter(this._diagram);
  }
  this._suspendAnimation = false;
  return consumed;
};

DvtAdfDiagramClientBehaviorHandler.prototype.CreateClientBehaviorEvent = function(target, behavior) {
  var behaviorEvent = DvtAdfDiagramClientBehaviorHandler.superclass.CreateClientBehaviorEvent.call(this, target, behavior);
  behaviorEvent.addParam('suppressAnimation', true);
  return behaviorEvent;
};

DvtAdfDiagramClientBehaviorHandler.prototype.suspendAnimation = function() {
  this._suspendAnimation = true;
};
// Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved.

/**
 * dvt.AdfDiagramControlPanelLAFUtils Utility class for providing LAF for the control panel.
 */
dvt.AdfDiagramControlPanelLAFUtils = {};

//dvt.Obj.createSubclass(DvtHvControlPanel, dvt.Obj, "DvtHvControlPanel");

//TODO button uris should be pased in xml and not hardcoded
dvt.AdfDiagramControlPanelLAFUtils.BUTTON_NAME_VIEW_TREE = 'viewtree';
dvt.AdfDiagramControlPanelLAFUtils.BUTTON_NAME_VIEW_TREE_HORIZONTAL = 'viewtreeHorizontal';
dvt.AdfDiagramControlPanelLAFUtils.BUTTON_NAME_VIEW_VERTICAL = 'viewtreeVertical';
dvt.AdfDiagramControlPanelLAFUtils.BUTTON_NAME_VIEW_RADIAL = 'viewtreeRadial';
dvt.AdfDiagramControlPanelLAFUtils.BUTTON_NAME_VIEW_CIRCLE = 'viewtreeCircle';
dvt.AdfDiagramControlPanelLAFUtils.BUTTON_NAME_VIEW_FORCE = 'viewtree_force';


/**
 * Create a shape (for a particular button state) for the layout button (used to show the layout combo).
 *
 * @param  {Number}          state   the button state (see dvt.ImageLAFUtils.xxx)
 * @param  {String}          layout  the layout type  (see dvt.HierarchyViewer.LAYOUT_xxx)
 * @return {dvt.Displayable}  shape   representing the layout button state.
 */
dvt.AdfDiagramControlPanelLAFUtils.createLayoutButtonState = function(context, state, layout, images, styleMap)
{
  var bname = dvt.AdfDiagramControlPanelLAFUtils.ConvertLayoutToButtonIconName(layout);

  return dvt.ButtonLAFUtils.createControlButtonState(context, bname, state, images, styleMap);
};


/**
 * Create a shape (for a particular button state) used in the layout dropdown.
 *
 * @param  {Number}         state   the button state (see dvt.ImageLAFUtils.xxx)
 * @param  {String}         layout  the layout type  (see dvt.HierarchyViewer.LAYOUT_xxx)
 * @return {dvt.Displayable} shape   representing the layout item state.
 */
dvt.AdfDiagramControlPanelLAFUtils.createLayoutItemState = function(context, state, layout, images, styleMap)
{
  var bname = dvt.AdfDiagramControlPanelLAFUtils.ConvertLayoutToItemIconName(layout, true);

  return dvt.ButtonLAFUtils.createLayoutItemButtonState(context, dvt.ButtonLAFUtils.BUTTON_TYPE_CONTROLS, bname, state, images, styleMap);
};


/**
 * Convert the layout to the name used by the icons for the layout button.
 *
 * @param  {String} layout   view layout
 * @return {String} name     used by icons for layout button
 */
dvt.AdfDiagramControlPanelLAFUtils.ConvertLayoutToButtonIconName = function(layout)
{
  var iconName = dvt.AdfDiagramControlPanelLAFUtils.ConvertLayoutToItemIconName(layout);

  if (iconName)
    iconName = iconName + 'Sel';

  return iconName;
};


/**
  * Convert the layout to the name used by the icons for the layout dropdown.
  *
  * @param {String}  layout view layout
  * @return {String} name used by icons for layout dropdown (or null if not found).
  */
dvt.AdfDiagramControlPanelLAFUtils.ConvertLayoutToItemIconName = function(layout)
{
  var ret = null;
  switch (layout)
  {
    case dvt.HierarchyViewer.LAYOUT_HIER_VERT_TOP: ret = dvt.AdfDiagramControlPanelLAFUtils.BUTTON_NAME_VIEW_TREE;
    break;
    case dvt.HierarchyViewer.LAYOUT_HIER_HORZ_START: ret = dvt.AdfDiagramControlPanelLAFUtils.BUTTON_NAME_VIEW_TREE_HORIZONTAL;
    break;
    case dvt.HierarchyViewer.LAYOUT_TREE: ret = dvt.AdfDiagramControlPanelLAFUtils.BUTTON_NAME_VIEW_VERTICAL;
    break;
    case dvt.HierarchyViewer.LAYOUT_RADIAL: ret = dvt.AdfDiagramControlPanelLAFUtils.BUTTON_NAME_VIEW_RADIAL;
    break;
    case dvt.HierarchyViewer.LAYOUT_CIRCLE: ret = dvt.AdfDiagramControlPanelLAFUtils.BUTTON_NAME_VIEW_CIRCLE;
    break;
  }

  return ret;
};


/**
 * Create a shape (for a particular button state) for the panelcard sync button (used
 * to show the panelcard sync combo).
 *
 * @param  {Number}          state   the button state (see dvt.ImageLAFUtils.xxx)
 * @param  {}                styleMap
 * @return {dvt.Displayable}  shape   representing the layout button state.
 */
dvt.AdfDiagramControlPanelLAFUtils.createPanelCardSyncButtonState = function(context, state, styleMap, images)
{
  return dvt.ButtonLAFUtils.createPanelCardButtonState(context, state, styleMap, images);
};


/**
  * Create a shape (for a particular button state) used in the panel card switcher dropdown.
  *
  * @param {dvt.Context} context Platform specific context object
  * @param {number} state Button state - ena, ovr, dwn
  * @param {number} ww Button width
  * @param {number} hh Button height
  * @param {object} styleMap The object containing style specifications for this component
  * @param {dvt.DomXmlNode} images The xml string that contains image urls for the control
  * @return {dvt.Displayable} shape   representing the switcher item state.
 */
dvt.AdfDiagramControlPanelLAFUtils.createPanelCardSyncItemState = function(context, state, ww, hh, styleMap, images)
{
  return dvt.ButtonLAFUtils.createPanelCardSyncItemState(context, state, ww, hh, styleMap, images);
};




// Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved.



/**
 * @constructor
 * ControlPanel for use with dvt.AdfDiagram.
 * This panel contains tools for zooming, panning and panel card sync.
 * @param {dvt.Context} context The rendering context
 * @param {dvt.PanZoomComponent} view The component that this control panel belongs to
 * @param {Number} state Whether the initial state is collapsed or expanded
 */
dvt.AdfDiagramControlPanel = function(context, view, state) {
  this.Init(context, view, state);
};

dvt.Obj.createSubclass(dvt.AdfDiagramControlPanel, dvt.ControlPanel, 'dvt.AdfDiagramControlPanel');


/*--------------------------------------------------------------------*/
/*  Init()                                                            */
/*--------------------------------------------------------------------*/
/**
 * Constructor
 *
 * @param {dvt.Context} context The rendering context
 * @param {dvt.PanZoomComponent} view The component that this control panel belongs to
 * @param {Number} state Whether the initial state is collapsed or expanded
 * @protected
 */
dvt.AdfDiagramControlPanel.prototype.Init = function(context, view, state)
{
  dvt.AdfDiagramControlPanel.superclass.Init.call(this, context, view, state);
};


/**
 * @override
 */
dvt.AdfDiagramControlPanel.prototype.PopulateHorzContentBar = function(contentContainer)
{
  dvt.AdfDiagramControlPanel.superclass.PopulateHorzContentBar.call(this, contentContainer);

  var combo;
  var currX = 0, currY = 0;

  //--------------------------------------//
  //  Create panel card switcher combo    //
  //--------------------------------------//

  if (! this._view.isSyncCardOff())
  {
    // Only create the panel card sync combo box if there is a panel card to sync

    combo = this.updateCardSelectorList(contentContainer);

    if (combo && (combo.getItemCount() > 1))
    {
      contentContainer.addChild(combo);
      combo.setTranslate(currX, currY);
      currY += 18;
    }

    this._panelCardSyncCombo = combo;
  }
};


/**
 * Handle a panel card switcher combo box event.
 * @param {dvt.ComboBoxEvent} event combo box event
 * @protected
 */
dvt.AdfDiagramControlPanel.prototype.HandlePanelCardSyncChange = function(event) {
  if (event.subtype == dvt.ComboBoxEvent.SUBTYPE_ITEM_CHANGE) {
    var item = this._panelCardSyncCombo.getItem(event.getIndex());

    this._view.syncPanelCards(item.getId(), item.getText(), item.getIcon());
  }
};


/**
 * Create the combo box for syncing all panel cards.
 * @param {dvt.Container} contentContainer  container object
 * @return {dvt.ComboBox} for syncing all panel cards
 */
dvt.AdfDiagramControlPanel.prototype.updateCardSelectorList = function(contentContainer)
{
  var aCardList = this._view.getCardSelectorList();
  var combo = this._panelCardSyncComboBox;

  contentContainer = (contentContainer == undefined ? null : contentContainer);

  if (aCardList != null && aCardList.length > 0)
  {
    if (combo)
    {
      if (! contentContainer) {
        contentContainer = combo.getParent();
      }
      this.destroyCardSelector();
    }

    combo = this.createPanelCardSyncComboBox(aCardList);

    if (contentContainer)
    {
      contentContainer.addChild(combo);
    }
  }

  return combo;
};


/**
 * Creates a combo box used to display panel card switcher on teh control panel
 * @param {array}  aCardList an array that contains an object for each panel card. Each object is an array as well that contains a tooltip/label and/or an icon.
 * @return {dvt.ComboBox} a combo box used to display panel card switcher on teh control panel
 */
dvt.AdfDiagramControlPanel.prototype.createPanelCardSyncComboBox = function(aCardList)
{
  // If we don't have text or icons for any of the showDetailItems, don't show
  // the panel card sync combo box.

  if (aCardList == null || aCardList.length < 1) {
    return null;
  }

  var context = this._view.getCtx();
  var styleMap = this._view.getSubcomponentStyles();
  var combo = new dvt.ComboBox(context, null, styleMap);
  var images = this.getButtonImages();

  combo.setEventManager(this._eventManager);
  combo.setStaticButtonStates(dvt.AdfDiagramControlPanelLAFUtils.createPanelCardSyncButtonState(context, dvt.Button.STATE_ENABLED, styleMap, images),
      dvt.AdfDiagramControlPanelLAFUtils.createPanelCardSyncButtonState(context, dvt.Button.STATE_OVER, styleMap, images),
      dvt.AdfDiagramControlPanelLAFUtils.createPanelCardSyncButtonState(context, dvt.Button.STATE_DOWN, styleMap, images));

  var indexWidestItem = -1;       // index of the widest item in the array
  var maxLabelWidth = 0;        // max label width of all the items
  var oTxt;                       // dvt.OutputText used to determine label width
  var bAnyIcons = false;    // flag indicating whether any items show icons
  // so that we can save space in all items
  var dim;
  var i;

  // Loop over items first time to save info, and to figure out which item
  // will be the widest so that all the items can be drawn to the same width.


  for (i = 0; i < aCardList.length; i++)
  {
    var aCard = aCardList[i];
    var sTooltip = aCard[0];
    var sIcon = aCard[1];

    if ((sIcon != null) && (sIcon.length > 0)) {
      bAnyIcons = true;
    }

    // BUG FIX #7352463: show item in dropdown if there is a static icon or static text
    // if the tooltip/label is not null, determine whether it is the widest label.

    if ((sTooltip != null) && (sTooltip.length > 0))
    {
      oTxt = dvt.PanelCardComboBoxItem.createLabelText(context, sTooltip, null, styleMap);  // used to calc width

      dim = oTxt.measureDimensions();
      if (dim.w > maxLabelWidth)        // if this is the longest label so far, save
      {                                 // width and index of the item that contains it
        maxLabelWidth = dim.w;
        indexWidestItem = i;    //aItems.length - 1;
      }
    }
  }


  var aSingleItem;                       // array of info for a single item
  var widestItem;                        // widest dvt.PanelCardComboBoxItem
  var widestItemWidth = -1;              // width of widest item

  // If there is a widest item with a label, create the item and save its total width
  if (indexWidestItem > -1)
  {
    aSingleItem = aCardList[indexWidestItem];
    widestItem = this.createDvtPanelCardComboBoxItem(aSingleItem[0], aSingleItem[1], bAnyIcons, null);
    dim = dvt.DisplayableUtils.getDimensionsForced(context, widestItem);
    widestItemWidth = dim.w;
  }

  //  Add all the items to the combo box
  var pccbItem;
  for (i = 0; i < aCardList.length; i++)
  {
    aSingleItem = aCardList[i];

    // If this is the widest item, add the item already created to the combo;
    // don't need to specify width because this item is the widest one already.

    if (i == indexWidestItem) {
      combo.addItem(widestItem, aSingleItem[0]);
    }
    else      // Create all other non-widest items as  widest item width, and add to combo.
    {
      pccbItem = this.createDvtPanelCardComboBoxItem(aSingleItem[0], aSingleItem[1], bAnyIcons, widestItemWidth);
      combo.addItem(pccbItem, aSingleItem[0]);
    }
  }
  var tooltip = dvt.Bundle.getTranslatedString(dvt.Bundle.SUBCOMPONENT_PREFIX, 'CONTROL_PANEL_SYNC');
  this._eventManager.associate(combo, new dvt.SimpleObjPeer(tooltip));

  // Add the listener after setting the selected index so that we don't get an event right now
  combo.addEvtListener(dvt.ComboBoxEvent.TYPE, this.HandlePanelCardSyncChange, false, this);

  return combo;
};


/**
 * Create an item to add to the panel card sync combo box.
 *
 * @param {string}  sLabel  text label for the item
 * @param {boolean} sIcon   icon URL for the item
 * @param {boolean} bSaveIconSpace  true if space should be saved for an icon, else false.
 * @param {number}  ww      optional width to make the item
 * @return {dvt.PanelCardComboBoxItem}  dvt.PanelCardComboBoxItem  obj to add to the combo box
 */
dvt.AdfDiagramControlPanel.prototype.createDvtPanelCardComboBoxItem = function(sLabel, sIcon, bSaveIconSpace, ww)
{
  ww = (ww == undefined ? -1 : ww);

  var styleMap = this._view.getSubcomponentStyles();
  var context = this._view.getCtx();
  var item = new dvt.PanelCardComboBoxItem(context, null, bSaveIconSpace, sLabel, sIcon, ww, styleMap, this.getButtonImages());
  return item;
};


/**
 * Release resources.
 */
dvt.AdfDiagramControlPanel.prototype.destroyCardSelector = function()
{
  var combo = this._panelCardSyncComboBox;

  if (combo)
  {
    combo.closeDropdown();

    // remove panelCard item listeners
    combo.destroy();
    combo.removeEvtListener(dvt.ComboBoxEvent.TYPE, this.HandlePanelCardSyncChange);

    // detach from parent
    var comboParent = combo.getParent();
    if (comboParent != null)
      comboParent.removeChild(combo);
  }

};

/**
 * @override
 */
dvt.AdfDiagramControlPanel.prototype.isSingleHorzRow = function() {
  return true;
};

/**
 * @override
 */
dvt.AdfDiagramControlPanel.prototype.getActionDisplayable = function(type, stampId) {
  var displayable = dvt.AdfDiagramControlPanel.superclass.getActionDisplayable.call(this, type, stampId);
  if (!displayable && this._panelCardSyncCombo && type == 'viewMenu') {
    if (stampId != null) {
      //PanelCardSyncComboBox button is disabled when menu is open
      if (!this._panelCardSyncCombo.getButton().isMouseEnabled()) {
        var idx = this._view.convertPanelCardIdToIndex(stampId);
        displayable = this._panelCardSyncCombo.getItem(idx);
      }
    } else {
      displayable = this._panelCardSyncCombo.getButton();
    }
  }
  return displayable;
};

/*------------------------------------------------------------------------*/
/*  dvt.PanelCardComboBoxItem      Represents an item in the panel card sync  */
/*                             combo box                                  */
/*------------------------------------------------------------------------*/
/**
 * @constructor
 * Represents an item in the panel card sync combo box.
 * @param {dvt.Context} context  context object
 * @param {string}  sId        id of the showDetailItem item represents.
 * @param {boolean} bSaveIconSpace  true if space should be saved for an icon, else false.
 * @param {string}  sText      text label for the item
 * @param {string}  sIcon      icon URL for the item
 * @param {number}  ww         optional width to make the item
 * @param {object}  styleMap   style map
 * @param {object}  images     The object containing button images
 */
dvt.PanelCardComboBoxItem = function(context, sId, bSaveIconSpace, sText, sIcon, ww, styleMap, images)
{
  this.Init(context, sId, bSaveIconSpace, sText, sIcon, ww, styleMap, images);
};

dvt.Obj.createSubclass(dvt.PanelCardComboBoxItem, dvt.Container, 'dvt.PanelCardComboBoxItem');


/**
 * @protected
 * Initialize the panel card sync combo box item.
 * @param {dvt.Context} context  context object
 * @param {string}  sId        id of the showDetailItem this item represents.
 * @param {boolean} bSaveIconSpace  true if space should be saved for an icon, else false.
 * @param {string}  sText      text label for the item
 * @param {string}  sIcon      icon URL for the item
 * @param {number}  ww         optional width to make the item
 * @param {object}  styleMap   style map
 * @param {object}  images     The object containing button images
 */
dvt.PanelCardComboBoxItem.prototype.Init = function(context, sId, bSaveIconSpace, sText, sIcon, ww, styleMap, images)
{
  dvt.PanelCardComboBoxItem.superclass.Init.call(this, context, null, sId);
  this._sText = sText;
  this._sIcon = sIcon;
  this._images = images;

  this._styleMap = (styleMap == undefined ? null : styleMap);
  var button = new dvt.Button(context,
      this.createItemState(context, dvt.Button.STATE_ENABLED, sText, sIcon, bSaveIconSpace, ww),
      this.createItemState(context, dvt.Button.STATE_OVER, sText, sIcon, bSaveIconSpace, ww),
      this.createItemState(context, dvt.Button.STATE_DOWN, sText, sIcon, bSaveIconSpace, ww),
      null, sText);
  this.addChild(button);
};


/**
 * Get the text of the showDetailItem for this panel card item.
 * @return {string}  text of the showDetailItem for this panel card item.
 */
dvt.PanelCardComboBoxItem.prototype.getText = function()
{
  return this._sText;
};


/**
 * Get the icon of the showDetailItem for this panel card item.
 * @return {string} icon of the showDetailItem for this panel card item.
 */
dvt.PanelCardComboBoxItem.prototype.getIcon = function()
{
  return this._sIcon;
};


/**
 * Handle the icon once it's loaded.
 * @param {object} bitmap bitmap of the icon
 */
dvt.PanelCardComboBoxItem.prototype.onIconLoaded = function(bitmap)
{
  var iconWidth = dvt.ButtonLAFUtils.CONTROL_BUTTON_WIDTH;
  var iconHeight = dvt.ButtonLAFUtils.CONTROL_BUTTON_HEIGHT;

  if (bitmap != null)
  {
    //if the bitmap is too big for the combo box, scale it
    if (bitmap.width > iconWidth || bitmap.height > iconHeight)
    {
      //find the dimension with the largest scale difference
      var sx = bitmap.width / iconWidth;
      var sy = bitmap.height / iconHeight;
      var newScale = Math.max(sx, sy);

      //scale both dimensions the same to maintain aspect ratio
      bitmap.scaleX = 1.0 / newScale;
      bitmap.scaleY = 1.0 / newScale;
    }

    //center the bitmap
    bitmap.x = (iconWidth - bitmap.width) / 2.0;
    bitmap.y = (iconHeight - bitmap.height) / 2.0;
  }
};


/**
 * Creates an item state for the panel card switcher dropdown
 * @param {dvt.Context} context Platform specific context object
 * @param {number} state State of the item
 * @param {string} sText Item text label
 * @param {string} sIcon Item image url
 * @param {boolean} bSaveIconSpace True if the text should leave the space for the image
 * @param {number} ww Item width
 * @return {dvt.Container} container that holds the item state
 */
dvt.PanelCardComboBoxItem.prototype.createItemState = function(context, state, sText, sIcon, bSaveIconSpace, ww)
{
  var itemHeight = dvt.StyleUtils.getStyle(this._styleMap['comboBox'], 'itemHeight', 0);
  var imagePadding = dvt.StyleUtils.getStyle(this._styleMap['comboBox'], 'imagePadding', 0);
  var itemPadding = dvt.StyleUtils.getStyle(this._styleMap['comboBox'], 'itemPadding', 0);
  var controlButtonWidth = dvt.ButtonLAFUtils.CONTROL_BUTTON_WIDTH;
  var controlButtonHeight = dvt.ButtonLAFUtils.CONTROL_BUTTON_HEIGHT;

  var buttonState = new dvt.Container(context);
  var image = null;
  var oTxt = null;
  var textDims = null;

  //create image
  if ((sIcon != null) && (sIcon.length > 0))
    image = new dvt.Image(this.getCtx(), this.getIcon(),
        imagePadding, imagePadding,
        controlButtonWidth - imagePadding * 2, controlButtonHeight - imagePadding * 2);

  //create text
  if (sText != null) {
    oTxt = dvt.PanelCardComboBoxItem.createLabelText(context, sText, state, this._styleMap);
    textDims = oTxt.measureDimensions();
  }

  if (ww < 0) { // calculate the size if the size is not set
    ww = textDims ? textDims.w + 2 * itemPadding : 0;
    ww += bSaveIconSpace ? controlButtonWidth + itemPadding : 0;
  }

  //create base - border/background - according to style map and state
  var base = dvt.AdfDiagramControlPanelLAFUtils.createPanelCardSyncItemState(context, state, ww, itemHeight, this._styleMap, this._images);
  buttonState.addChild(base);

  var transX = 0;
  if (image) {
    transX = dvt.Agent.isRightToLeft(context) ? ww - controlButtonWidth - itemPadding : itemPadding;
    image.setTranslate(transX, (itemHeight - controlButtonHeight) / 2);

    buttonState.addChild(image);
    dvt.ImageLoader.loadImage(context, sIcon, dvt.Obj.createCallback(this, this.onIconLoaded));
  }
  if (oTxt != null) {
    if (dvt.Agent.isRightToLeft(context))
      transX = bSaveIconSpace ? ww - textDims.w - 2 * itemPadding - controlButtonWidth : ww - textDims.w - itemPadding;
    else
      transX = bSaveIconSpace ? 2 * itemPadding + controlButtonWidth : itemPadding;
    oTxt.setTranslate(transX, (itemHeight - textDims.h) / 2);
    buttonState.addChild(oTxt);
  }

  return buttonState;
};


/**
 * Static method to create a dvt.OutputText object for the label in an item.
 * @param {dvt.Context} context Platform specific context object
 * @param {string}  sLabel  item text label.
 * @param {number} state button state
 * @param {object} styleMap The object containing style specifications for this component
 * @return {dvt.OutputText}  text object for given text.
 */
dvt.PanelCardComboBoxItem.createLabelText = function(context, sLabel, state, styleMap)
{
  var t = new dvt.OutputText(context, sLabel);
  var style = new dvt.CSSStyle();
  style.setStyle(dvt.CSSStyle.FONT_SIZE, dvt.AfStyleUtils.getDefaultFontSize());
  style.setStyle(dvt.CSSStyle.FONT_FAMILY, dvt.AfStyleUtils.getDefaultFontFamily());
  t.setCSSStyle(style);
  return t;
};
/**
 *  Provides automation services for a DVT diagram component.
 *  @class  dvt.AdfDiagramAutomationBase
 *  @param {dvt.AdfDiagram} dvtComponent
 *  @implements {dvt.Automation}
 *  @constructor
 */
dvt.AdfDiagramAutomationBase = function(dvtComponent) {
  this.Init(dvtComponent);
};

dvt.Obj.createSubclass(dvt.AdfDiagramAutomationBase, dvt.Automation, 'dvt.AdfDiagramAutomationBase');

/**
 * Initializes this automation object
 * @param {dvt.AdfDiagram} dvtComponent
 */
dvt.AdfDiagramAutomationBase.prototype.Init = function(dvtComponent) {
  this._diagram = dvtComponent;
};

/**
 * Gets the component
 * @return {dvt.AdfDiagram} the component
 */
dvt.AdfDiagramAutomationBase.prototype.getComponent = function() {
  return this._diagram;
};

/**
 * Finds the AF component with the given id.
 * @param {dvt.AfComponent} afComp The dvt.AfComponent tree to search for a matching id
 * @param {String} id The AF component id
 * @return {dvt.AfComponent}
 * @protected
 */
dvt.AdfDiagramAutomationBase.prototype.FindAfComponent = function(afComp, id) {
  var matchedChild = null;
  if (afComp)
    matchedChild = dvt.AfComponentFactory.findAfComponent(afComp, id);
  return matchedChild;
};

/**
 * @protected
 * Create control panel sub id for the action and stampId
 * @param {String} action  control panel action type
 * @param {String} stampId  item stamp id if the action is performed on a combo box
 * @return {String} control panel sub id
 */
dvt.AdfDiagramAutomationBase.prototype.CreateControlPanelSubId = function(action, stampId) {
  var subId = 'controlPanel';
  if (action)
    subId += '#' + action;
  if (stampId)
    subId += ':' + stampId;
  return subId;
};

/**
 * @protected
 * Get the Control panel sub id for the displayable
 * @param {dvt.ControlPanel} controlPanel  control panel component
 * @param {dvt.Displayable} displayable  original displayable element
 * @return {String} control panel sub id
 */
dvt.AdfDiagramAutomationBase.prototype.GetControlPanelSubId = function(controlPanel, displayable) {
  //If displayable is in control panel
  if (controlPanel) {
    var logicalObj = controlPanel.getEventManager().GetLogicalObject(displayable);
    if (logicalObj && logicalObj instanceof dvt.Button) {
      var actions = ['disclosure', 'zoomIn', 'zoomOut', 'zoomToFit'];
      for (var idx = 0; idx < actions.length; idx++) {
        if (controlPanel.getActionDisplayable(actions[idx]) === logicalObj) {
          return this.CreateControlPanelSubId(actions[idx]);
        }
      }
    }
  }
  return null;
};
/**
 *  Provides automation services for a DVT diagram component.
 *  @class  DvtAdfDiagramAutomation
 *  @param {dvt.AdfDiagram} dvtComponent
 *  @implements {dvt.Automation}
 *  @constructor
 */
var DvtAdfDiagramAutomation = function(dvtComponent) {
  this.Init(dvtComponent);
};

dvt.Obj.createSubclass(DvtAdfDiagramAutomation, dvt.AdfDiagramAutomationBase, 'DvtAdfDiagramAutomation');

/**
 * Valid subIds include:
 * <ul>
 * <li>breadcrumbs[index]</li>
 * <li>restore</li>
 * <li>link[rowIndex]</li>
 * <li>link[rowIndex]:stampId</li>
 * <li>node[rowIndex]</li>
 * <li>node[rowIndex]:stampId</li>
 * <li>node[rowIndex]#drill</li>
 * <li>node[rowIndex]#isolate</li>
 * <li>node[rowIndex]#additionalLinks</li>
 * <li>node[rowIndex]#stacking</li>
 * <li>node[rowIndex]#disclosure</li>
 * <li>controlPanel#disclosure</li>
 * <li>controlPanel#zoomToFit</li>
 * <li>controlPanel#zoomIn</li>
 * <li>controlPanel#zoomOut</li>
 * <li>controlPanel#viewMenu</li>
 * <li>controlPanel#viewMenu:stampId</li>
 * </ul>
 * @override
 */
DvtAdfDiagramAutomation.prototype.GetSubIdForDomElement = function(displayable) {
  if (!displayable)
    return null;

  var subComponent = this._getSubComponent(displayable);
  if (!subComponent)
    return null;

  //If the displayable is an AF component, get the AF Component
  var afComp = dvt.AfComponentFactory.getAfComponent(displayable);
  while (displayable && !(displayable instanceof dvt.AdfDiagram)) {
    //Diagram Node displayable
    if (subComponent instanceof dvt.AdfDiagramNode) {
      if (displayable instanceof dvt.AdfDiagramNode)
        return this._createSubId('node', subComponent.getData().getRowIndex());

      var action = (displayable instanceof dvt.Button && displayable.getId() == 'disclosure') ? 'disclosure' : null;
      if (afComp || action)
        return this._createSubId('node', subComponent.getData().getRowIndex(), afComp ? afComp.getId() : null, action);
    }
    //Diagram Link displayable
    else if (subComponent instanceof dvt.AdfDiagramLink) {
      return this._createSubId('link', subComponent.getData().getRowIndex(), afComp ? afComp.getId() : null);
    }
    //Diagram displayable
    else if (subComponent instanceof dvt.AdfDiagram) {
      if (displayable instanceof DvtAdfDiagramFlyOutButton) {
        var node = this.getComponent().getNodeById(this.getComponent().GetTransientToolbarDataId());
        if (node) {
          var buttonId = displayable.getId();
          action = (buttonId == 'unstacking' || buttonId == 'restacking') ? 'stacking' : buttonId;
          return this._createSubId('node', node.getData().getRowIndex(), null, action);
        }
      }
      if (displayable instanceof dvt.Button && displayable.getId() == 'restore')
        return this._createSubId('restore');
      var parent = displayable.getParent();
      if (parent instanceof dvt.Breadcrumbs)
        return this._createSubId('breadcrumbs', parent.getCrumbIndex(displayable));
    }
    //Control Panel Displayable
    else if (subComponent instanceof dvt.AdfDiagramControlPanel) {
      var subId = this.GetControlPanelSubId(displayable);
      if (subId)
        return subId;
    }
    displayable = displayable.getParent();
  }
  return null;
};

/**
 * @override
 */
DvtAdfDiagramAutomation.prototype.GetControlPanelSubId = function(displayable) {
  var controlPanel = this.getComponent().getPanZoomCanvas().getControlPanel();
  if (!controlPanel || !displayable)
    return null;

  //Control panel buttons
  var subId = DvtAdfDiagramAutomation.superclass.GetControlPanelSubId.call(this, controlPanel, displayable);
  if (!subId) {
    //View Menu
    var action = 'viewMenu';
    var viewMenu = controlPanel.getActionDisplayable(action);
    if (viewMenu) {
      //Combo box button
      if (displayable === viewMenu) {
        subId = this.CreateControlPanelSubId(action);
      //Combo box items
      } else if (displayable instanceof dvt.PanelCardComboBoxItem) {
        var items = viewMenu.getParent().getItems();
        for (var i = 0; i < items.length; i++) {
          if (items[i] === displayable) {
            var stampId = this.getComponent().convertIndexToPanelCardId(i);
            return this.CreateControlPanelSubId(action, stampId);
          }
        }
      }
    }
  }
  return subId;
};

/**
 * @private
 * Get the sub component from the displayable
 * This method identifies the sub-component that displayable belongs to.
 *
 * @param {dvt.Displayable} displayable  displayable object
 * @return {dvt.Displayable} The sub component displayable
 */
DvtAdfDiagramAutomation.prototype._getSubComponent = function(displayable)
{
  var parent = displayable;
  while (parent)
  {
    if (parent instanceof dvt.AdfDiagram ||
        parent instanceof dvt.AdfDiagramNode ||
        parent instanceof dvt.AdfDiagramLink ||
        parent instanceof dvt.AdfDiagramControlPanel) {
      return parent;
    }
    if (!parent.getParent)
      break;
    parent = parent.getParent();
  }
  return null;
};

/**
 * Valid subIds include:
 * <ul>
 * <li>breadcrumbs[index]</li>
 * <li>restore</li>
 * <li>link[rowIndex]</li>
 * <li>link[rowIndex]:stampId</li>
 * <li>node[rowIndex]</li>
 * <li>node[rowIndex]:stampId</li>
 * <li>node[rowIndex]#drill</li>
 * <li>node[rowIndex]#isolate</li>
 * <li>node[rowIndex]#additionalLinks</li>
 * <li>node[rowIndex]#stacking</li>
 * <li>node[rowIndex]#disclosure</li>
 * <li>controlPanel#disclosure</li>
 * <li>controlPanel#zoomToFit</li>
 * <li>controlPanel#zoomIn</li>
 * <li>controlPanel#zoomOut</li>
 * <li>controlPanel#viewMenu</li>
 * <li>controlPanel#viewMenu:stampId</li>
 * </ul>
 * @override
 */
DvtAdfDiagramAutomation.prototype.getDomElementForSubId = function(subId) {
  var parsedSubId = this._parseSubId(subId);
  var component = parsedSubId['component'];
  var index = parsedSubId['index'];
  var action = parsedSubId['action'];
  var stampId = parsedSubId['stampId'];
  var displayable = null;

  if (component == 'link') {
    displayable = this._findLinkDisplayable(index, stampId);
  }
  else if (component == 'node') {
    displayable = this._findNodeDisplayable(index, stampId, action);
  }
  else if (component == 'breadcrumbs') {
    displayable = this.getComponent().getBreadcrumbs().getCrumb(index);
  }
  else if (component == 'restore') {
    displayable = this.getComponent().getRestoreButton().getChildAt(0);
  }
  else if (component == 'controlPanel') {
    var controlPanel = this.getComponent().getPanZoomCanvas().getControlPanel();
    if (controlPanel && action)
      displayable = controlPanel.getActionDisplayable(action, stampId);
  }

  return displayable ? displayable.getElem() : null;
};

/**
 * Finds the link-related dvt.Displayable with the given subId.
 * @param {number} index The link index
 * @param {String} stampId The AF component stampId
 * @return {dvt.Displayable}
 * @private
 */
DvtAdfDiagramAutomation.prototype._findLinkDisplayable = function(index, stampId) {
  var link = this.getComponent().GetLinkByRowIndex(index);
  if (link) {
    if (stampId) {
      var afComp;
      // check end connector
      var endConnector = link.getEndConnector();
      afComp = dvt.AfComponentFactory.getAfComponent(endConnector);
      if (afComp && afComp.getId() == stampId)
        return endConnector;
      // check start connector
      var startConnector = link.getStartConnector();
      afComp = dvt.AfComponentFactory.getAfComponent(startConnector);
      if (afComp && afComp.getId() == stampId)
        return startConnector;
    } else {
      return link;
    }
  }
  return null;
};

/**
 * Finds the node-related dvt.Displayable with the given subId.
 * @param {number} index The node index
 * @param {String} stampId The AF component stampId
 * @param {String} action The node button id
 * @return {dvt.Displayable}
 * @private
 */
DvtAdfDiagramAutomation.prototype._findNodeDisplayable = function(index, stampId, action) {
  var node = this.getComponent().GetNodeByRowIndex(index);
  if (node) {
    if (stampId) {
      var afComp = node.getAfComponent(stampId);
      if (afComp)
        return afComp.getDisplayObj();
    }
    else if (action) {
      if (action == 'disclosure')
        return node.getContainerBadge();
      else
        return node.getFlyOutButtonById(action);
    }
    else {
      return node;
    }
  }
  return null;
};

/**
 * Parses a Diagram subId string into a map of component, index, stampId, and action items.
 * @param {String} subId The Diagram subId to parse
 * @return {Object}
 * @private
 */
DvtAdfDiagramAutomation.prototype._parseSubId = function(subId) {
  var component = subId;
  var index = -1;
  var stampId = null;
  var action = null;

  var substring = subId.substring(0, subId.indexOf('['));
  if (substring) {
    switch (substring) {
      case 'node':
      case 'link':
      case 'breadcrumbs':
        component = substring;
        break;
      default:
        component = null;
    }
    index = parseInt(subId.substring(subId.indexOf('[') + 1, subId.indexOf(']')));
  } else if (component.indexOf('controlPanel') == 0) {
    component = 'controlPanel';
  }

  if (component == 'link' || component == 'node') {
    var colonIdx = subId.indexOf(':');
    if (colonIdx !== -1) {
      stampId = subId.substring(colonIdx + 1);
    } else {
      var hashIdx = subId.indexOf('#');
      if (hashIdx !== -1)
        action = subId.substring(hashIdx + 1);
    }
  } else if (component == 'controlPanel') {
    var hashIdx = subId.indexOf('#');
    if (hashIdx !== -1) {
      var colonIdx = subId.indexOf(':');
      if (colonIdx !== -1)
        stampId = subId.substring(colonIdx + 1);
      action = colonIdx != -1 ? subId.substring(hashIdx + 1, colonIdx) : subId.substring(hashIdx + 1);
    }
  }

  return {'component': component, 'index': index, 'stampId': stampId, 'action': action};
};


/**
 * Creates a Diagram subId from the component, index, stampId, and action items.
 * @param {String} component The component type e.g. node, link, breadcrumbs, restore
 * @param {number} index The component index
 * @param {String} stampId The AF component stampId
 * @param {String} action The component button id
 * @return {String}
 * @private
 */
DvtAdfDiagramAutomation.prototype._createSubId = function(component, index, stampId, action) {
  var subId = component;
  if (index != null)
    subId = subId + '[' + index + ']';

  if (stampId)
    subId = subId + ':' + stampId;
  else if (action)
    subId = subId + '#' + action;

  return subId;
};

/**
 * Get the client component properties from the specified dvt.AfComponent
 * @param {dvt.AfComponent} afComponent The dvt.AfComponent
 * @return {array} a two-element array containing 1) the dvt.AfComponent type name (which will be mapped by the peer into
 * the corresponding client component constructor and 2) the array of arguments that should be passed to the constructor
 */
DvtAdfDiagramAutomation.prototype.getClientComponentProperties = function(afComponent) {
  if (!afComponent || !afComponent.getTypeName)
    return null;
  var typeName = afComponent.getTypeName();
  if (typeName == 'dvt.AfButton' ||
      typeName == 'dvt.AfImage' ||
      typeName == 'dvt.AfLink' ||
      typeName == 'dvt.AfOutputFormatted' ||
      typeName == 'dvt.AfOutputText' ||
      typeName == 'dvt.AfPanelFormLayout' ||
      typeName == 'dvt.AfPanelGroupLayout' ||
      typeName == 'dvt.AfPanelLabelAndMessage' ||
      typeName == 'dvt.AfSeparator' ||
      typeName == 'dvt.AfSpacer' ||
      typeName == 'dvt.AfCommandButton' ||
      typeName == 'dvt.AfCommandImageLink' ||
      typeName == 'dvt.AfCommandLink' ||
      typeName == 'dvt.AfGoButton' ||
      typeName == 'dvt.AfGoLink') {
    return [typeName, [afComponent.getId(), afComponent._props]];
  }
  else if (typeName == 'dvt.AfMarker' ||
           typeName == 'dvt.AfPanelCard') {
    return [typeName, [afComponent]];
  }
  return null;
};
/**
 * @constructor
 */
dvt.AdfDiagramChangeLinkEvent = function(link, oldEndpoint, newEndpoint, endpointType) {
  this.Init(dvt.AdfDiagramChangeLinkEvent.TYPE);
  this._link = link;
  this._oldEndpoint = oldEndpoint;
  this._newEndpoint = newEndpoint;
  this._endpointType = endpointType;
};

dvt.Obj.createSubclass(dvt.AdfDiagramChangeLinkEvent, dvt.BaseComponentEvent, 'dvt.AdfDiagramChangeLinkEvent');

dvt.AdfDiagramChangeLinkEvent.TYPE = 'diagramChangeLinkEvent';

dvt.AdfDiagramChangeLinkEvent.prototype.getLink = function() {
  return this._link;
};
dvt.AdfDiagramChangeLinkEvent.prototype.getOldEndpoint = function() {
  return this._oldEndpoint;
};
dvt.AdfDiagramChangeLinkEvent.prototype.getNewEndpoint = function() {
  return this._newEndpoint;
};
dvt.AdfDiagramChangeLinkEvent.prototype.getEndpointType = function() {
  return this._endpointType;
};
/**
 * @constructor
 */
dvt.AdfDiagramCreateLinkEvent = function(source, destination, paletteId) {
  this.Init(dvt.AdfDiagramCreateLinkEvent.TYPE);
  this._source = source;
  this._destination = destination;
  this._paletteId = paletteId;
};

dvt.Obj.createSubclass(dvt.AdfDiagramCreateLinkEvent, dvt.BaseComponentEvent, 'dvt.AdfDiagramCreateLinkEvent');

dvt.AdfDiagramCreateLinkEvent.TYPE = 'diagramCreateLinkEvent';

dvt.AdfDiagramCreateLinkEvent.prototype.getSource = function() {
  return this._source;
};

dvt.AdfDiagramCreateLinkEvent.prototype.getDestination = function() {
  return this._destination;
};

dvt.AdfDiagramCreateLinkEvent.prototype.getPaletteId = function() {
  return this._paletteId;
};

/**
 * @constructor
 */
dvt.AdfDiagramCreateNodeEvent = function(paletteId, parentId, position) {
  this.Init(dvt.AdfDiagramCreateNodeEvent.TYPE);
  this._paletteId = paletteId;
  this._parentId = parentId;
  this._position = position;
};

dvt.Obj.createSubclass(dvt.AdfDiagramCreateNodeEvent, dvt.BaseComponentEvent, 'dvt.AdfDiagramCreateNodeEvent');

dvt.AdfDiagramCreateNodeEvent.TYPE = 'nodeCreate';
dvt.AdfDiagramCreateNodeEvent.prototype.getPaletteId = function() {
  return this._paletteId;
};
dvt.AdfDiagramCreateNodeEvent.prototype.getParentId = function() {
  return this._parentId;
};
dvt.AdfDiagramCreateNodeEvent.prototype.getPosition = function() {
  return this._position;
};
/**
 * @constructor
 */
dvt.AdfDiagramDeleteNodeEvent = function(nodes) {
  this.Init(dvt.AdfDiagramDeleteNodeEvent.TYPE);
  this._nodes = nodes;
};

dvt.Obj.createSubclass(dvt.AdfDiagramDeleteNodeEvent, dvt.BaseComponentEvent, 'dvt.AdfDiagramDeleteNodeEvent');

dvt.AdfDiagramDeleteNodeEvent.TYPE = 'diagramNodeDeleteEvent';

dvt.AdfDiagramDeleteNodeEvent.prototype.getNodes = function() {
  return this._nodes;
};
/**
 * @constructor
 */
dvt.AdfDiagramDeleteLinkEvent = function(links) {
  this.Init(dvt.AdfDiagramDeleteLinkEvent.TYPE);
  this._links = links;
};

dvt.Obj.createSubclass(dvt.AdfDiagramDeleteLinkEvent, dvt.BaseComponentEvent, 'dvt.AdfDiagramDeleteLinkEvent');

dvt.AdfDiagramDeleteLinkEvent.TYPE = 'diagramLinkDeleteEvent';

dvt.AdfDiagramDeleteLinkEvent.prototype.getLinks = function() {
  return this._links;
};
// Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
var DvtAdfDiagramEndpointUtils = {};

dvt.Obj.createSubclass(DvtAdfDiagramEndpointUtils, dvt.Obj, 'DvtAdfDiagramEndpointUtils');

DvtAdfDiagramEndpointUtils.ENDPOINT_RADIUS = 7.5;

DvtAdfDiagramEndpointUtils.ENDPOINT_NODE_BORDER_STROKE = 2;
DvtAdfDiagramEndpointUtils.ENDPOINT_NODE_BORDER_COLOR_ATTR_ENA = 'endpointNodeBorderEna';
DvtAdfDiagramEndpointUtils.ENDPOINT_NODE_BG_COLOR_ATTR_ENA = 'endpointNodeBgEna';

DvtAdfDiagramEndpointUtils.ENDPOINT_NODE_BORDER_COLOR_ATTR_DWN = 'endpointNodeBorderDwn';
DvtAdfDiagramEndpointUtils.ENDPOINT_NODE_BG_COLOR_ATTR_DWN = 'endpointNodeBgDwn';

DvtAdfDiagramEndpointUtils.ENDPOINT_LINK_BORDER_STROKE = 1;
DvtAdfDiagramEndpointUtils.ENDPOINT_LINK_BORDER_COLOR_ATTR = 'endpointLinkBorder';
DvtAdfDiagramEndpointUtils.ENDPOINT_LINK_BG_COLOR_ATTR = 'endpointLinkBg';


/**
 * Utility function that creates an object (a button in this case) to be used as a node endpoint or a link endpoint
 * @param {dvt.Context} context Platform specific context object
 * @param {string} type The endpoint type - DvtAdfDiagramNodeEndpoint.NODE_TYPE, DvtAdfDiagramLinkEndpoint.START_TYPE or DvtAdfDiagramLinkEndpoint.END_TYPE
 * @param {string} id Optional id for the created object
 * @param {object} styleMap The object containing style specifications for this component
 */
DvtAdfDiagramEndpointUtils.createEndpointShape = function(context, type, id, styleMap) {
  var badgeRad = DvtAdfDiagramEndpointUtils.ENDPOINT_RADIUS;
  var underlayRad = dvt.Agent.isTouchDevice() ? DvtAdfDiagramEndpointUtils.ENDPOINT_RADIUS * 2 : DvtAdfDiagramEndpointUtils.ENDPOINT_RADIUS;
  var shapeOffset = underlayRad - badgeRad;
  id = 'linkhandle_' + type + '_' + id;

  //styles
  var strokeWidth = DvtAdfDiagramEndpointUtils.getStrokeWidth(type, styleMap);
  var strokeEna = DvtAdfDiagramEndpointUtils.getStrokeColor(type, dvt.Button.STATE_ENABLED, styleMap);
  var strokeDwn = DvtAdfDiagramEndpointUtils.getStrokeColor(type, dvt.Button.STATE_DOWN, styleMap);
  var fillEna = DvtAdfDiagramEndpointUtils.getFillColor(type, dvt.Button.STATE_ENABLED, styleMap);
  var fillDwn = DvtAdfDiagramEndpointUtils.getFillColor(type, dvt.Button.STATE_DOWN, styleMap);

  var upShape = DvtAdfDiagramEndpointUtils._createShape(context, badgeRad, fillEna, strokeEna, strokeWidth, shapeOffset);
  var overShape = DvtAdfDiagramEndpointUtils._createShape(context, badgeRad, fillEna, strokeEna, strokeWidth, shapeOffset);
  var downShape = DvtAdfDiagramEndpointUtils._createShape(context, badgeRad, fillDwn, strokeDwn, strokeWidth, shapeOffset);

  var upState = new dvt.Container(context);
  upState.addChild(DvtAdfDiagramEndpointUtils._createUnderlay(context, underlayRad));
  upState.addChild(upShape);

  var overState = new dvt.Container(context);
  overState.addChild(DvtAdfDiagramEndpointUtils._createUnderlay(context, underlayRad));
  overState.addChild(overShape);

  var downState = new dvt.Container(context);
  downState.addChild(DvtAdfDiagramEndpointUtils._createUnderlay(context, underlayRad));
  downState.addChild(downShape);

  var button = new dvt.Button(context, upState, overState, downState, null, id);
  return button;
};


/**
 * Gets an enpoint offset - an distance from endpoint center
 * @return {number} endpoint offset
 */
DvtAdfDiagramEndpointUtils.getEndpointOffset = function(styleMap) {
  return (dvt.Agent.isTouchDevice() ? DvtAdfDiagramEndpointUtils.ENDPOINT_RADIUS * 2 : DvtAdfDiagramEndpointUtils.ENDPOINT_RADIUS);
};


/**
 * @private
 * Creates a shape for the endpoint state
 * @param {dvt.Context} context Platform specific context object
 * @param {number} radius Endpoint radius since it's a circle
 * @param {string} fillColor The hex or RGB representation of the solid color
 * @param {string} strokeColor The hex or RGB representation of the solid color
 * @param {number} strokeWidth Stroke width
 * @param {number} offset Shape offset
 */
DvtAdfDiagramEndpointUtils._createShape = function(context, radius, fillColor, strokeColor, strokeWidth, offset) {
  var shape = new dvt.Circle(context, radius, radius, radius);
  shape.setSolidFill(fillColor);
  shape.setSolidStroke(strokeColor, 1, strokeWidth);
  shape.setTranslate(offset, offset);
  return shape;
};


/**
 * @private
 * Creates an underlay for the endpoint state
 * @param {dvt.Context} context Platform specific context object
 * @param {number} radius Underlay radius since it's a circle
 */
DvtAdfDiagramEndpointUtils._createUnderlay = function(context, radius) {
  var shape = new dvt.Circle(context, radius, radius, radius);
  shape.setInvisibleFill();
  return shape;
};
DvtAdfDiagramEndpointUtils.getStrokeColor = function(type, state, styleMap) {
  var value = dvt.AdfDiagramNode._HOVER_SHADOW_COLOR;
  if (!styleMap)
    return value;

  if (type == DvtAdfDiagramNodeEndpoint.NODE_TYPE) { //node styles
    switch (state) {
      case dvt.Button.STATE_ENABLED:
        value = styleMap[DvtAdfDiagramEndpointUtils.ENDPOINT_NODE_BORDER_COLOR_ATTR_ENA];
        break;
      case dvt.Button.STATE_DOWN:
        value = styleMap[DvtAdfDiagramEndpointUtils.ENDPOINT_NODE_BORDER_COLOR_ATTR_DWN];
        break;
      default: value = dvt.AdfDiagramNode._HOVER_SHADOW_COLOR;
    }
  }
  else //link styles
    value = styleMap[DvtAdfDiagramEndpointUtils.ENDPOINT_LINK_BORDER_COLOR_ATTR];
  return value;
};

DvtAdfDiagramEndpointUtils.getFillColor = function(type, state, styleMap) {
  var value = '#FFFFFF';
  if (!styleMap)
    return value;

  if (type == DvtAdfDiagramNodeEndpoint.NODE_TYPE) { //node styles
    switch (state) {
      case dvt.Button.STATE_ENABLED:
        value = styleMap[DvtAdfDiagramEndpointUtils.ENDPOINT_NODE_BG_COLOR_ATTR_ENA];
        break;
      case dvt.Button.STATE_DOWN:
        value = styleMap[DvtAdfDiagramEndpointUtils.ENDPOINT_NODE_BG_COLOR_ATTR_DWN];
        break;
      default: value = '#FFFFFF';
    }
  }
  else //link styles
    value = styleMap[DvtAdfDiagramEndpointUtils.ENDPOINT_LINK_BG_COLOR_ATTR];
  return value;
};

DvtAdfDiagramEndpointUtils.getStrokeWidth = function(type, styleMap) {
  if (type == DvtAdfDiagramNodeEndpoint.NODE_TYPE)
    return DvtAdfDiagramEndpointUtils.ENDPOINT_NODE_BORDER_STROKE;
  else
    return DvtAdfDiagramEndpointUtils.ENDPOINT_LINK_BORDER_STROKE;
};


// Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
/**
 * @extends {dvt.Container}
 * @constructor
 * @class DvtAdfDiagramLinkEndpoint A link handle/endpoint used to edit a link ( move a link start or a link end to another node)
 * @param {dvt.AdfDiagramLink} parent Parent link
 * @param {string} type Handle type DvtAdfDiagramLinkEndpoint.START_TYPE or DvtAdfDiagramLinkEndpoint.END_TYPE
 * @param {dvt.Point} position Handle position
 * @param {string} id Optional id for the handle shape
 * @param {object} styleMap The object containing style specifications for this component
 */
var DvtAdfDiagramLinkEndpoint = function(context, link, type, position, id, styleMap)
{
  this.Init(context, link, type, position, id, styleMap);
};

dvt.Obj.createSubclass(DvtAdfDiagramLinkEndpoint, dvt.Container, 'DvtAdfDiagramLinkEndpoint');

DvtAdfDiagramLinkEndpoint.START_TYPE = 'start';
DvtAdfDiagramLinkEndpoint.END_TYPE = 'end';


/**
 * Initialization method called by the constructor
 * @param {dvt.Context} context Platform specific context object
 * @param {dvt.AdfDiagramLink} parent Parent link
 * @param {string} type Handle type DvtAdfDiagramLinkEndpoint.START_TYPE or DvtAdfDiagramLinkEndpoint.END_TYPE
 * @param {dvt.Point} position Handle position
 * @param {string} id Optional id for the handle shape
 * @param {object} styleMap The object containing style specifications for this component
 */
DvtAdfDiagramLinkEndpoint.prototype.Init = function(context, parent, type, position, id, styleMap)
{
  DvtAdfDiagramLinkEndpoint.superclass.Init.call(this, context, null, id + '_' + type + 'Hdl');
  this._link = parent;
  this._type = type;
  this._shape = DvtAdfDiagramEndpointUtils.createEndpointShape(this.getCtx(), this._type, id, styleMap);
  this._endpointOffset = DvtAdfDiagramEndpointUtils.getEndpointOffset(styleMap);
  this.addChild(this._shape);
  this.setPosition(position);

  //Fix for BUG 21148491: the diagram event manager uses mousedown and mouseup events to create or modify a diagram link.
  //The dvt.Button prevents the mousedown and mouseup events from bubling to the parent, so the diagram can't react on those events.
  //The separate listeners are added to the link endpoint button on capture phase in order to recieve and process the events.
  var eventManager = this._link.GetDiagram().getEventManager();
  this._shape.addEvtListener(dvt.MouseEvent.MOUSEDOWN, eventManager.OnMouseDown, true, eventManager);
  this._shape.addEvtListener(dvt.MouseEvent.MOUSEUP, eventManager.OnMouseUp, true, eventManager);
  eventManager.associate(this._shape, this);
};

DvtAdfDiagramLinkEndpoint.prototype.isLinkEndpoint = function()
{
  return true;
};

DvtAdfDiagramLinkEndpoint.prototype.HandleChangeStart = function(point)
{
  this._link.startLinkChange(this._type, point);
  this.RenderTempLink(point);
};

DvtAdfDiagramLinkEndpoint.prototype.setPosition = function(point) 
{
  this._shape.setTranslate(point.x - this._endpointOffset, point.y - this._endpointOffset);
};

DvtAdfDiagramLinkEndpoint.prototype.RenderTempLink = function(point)
{
  this._link.updateLinkChange(this._type, point);
  this.setMouseEnabled(false);
};

DvtAdfDiagramLinkEndpoint.prototype.HandleChangeMove = function(point)
{
  this.RenderTempLink(point);
};

DvtAdfDiagramLinkEndpoint.prototype.HandleChangeEndNewNode = function(centerPoint)
{
  this._link.endLinkChangeNewNode(this._type, centerPoint);
  this.setMouseEnabled(true);
};

DvtAdfDiagramLinkEndpoint.prototype.HandleChangeEndRestore = function()
{
  this._link.endLinkChangeRestore(this._type);
  this.setMouseEnabled(true);
};

DvtAdfDiagramLinkEndpoint.prototype.isUpdateEnabled = function() {
  return this._link.isUpdateEnabled();
};


/**
 * @override
 */
DvtAdfDiagramLinkEndpoint.prototype.isSelectable = function() {
  return this._link.isSelectable();
};


/**
 * @override
 */
DvtAdfDiagramLinkEndpoint.prototype.isSelected = function() {
  return this._link.isSelected();
};


/**
 * @override
 */
DvtAdfDiagramLinkEndpoint.prototype.setSelected = function(selected) {
  this._link.setSelected(selected);
};

DvtAdfDiagramLinkEndpoint.prototype.getLink = function()
{
  return this._link;
};

// Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
/**
 * @extends {dvt.Container}
 * @constructor
 * @class DvtAdfDiagramNodeEndpoint A node handle/endpoint used to create a link from the diagram node in editor mode
 * @param {dvt.Context} context Platform specific context object
 * @param {dvt.AdfDiagramNode} parent Parent node
 * @param {string} id Optional id for this object
 * @param {object} styleMap The object containing style specifications for this component
 */
var DvtAdfDiagramNodeEndpoint = function(context, parent, id, styleMap)
{
  this.Init(context, parent, id, styleMap);
};

dvt.Obj.createSubclass(DvtAdfDiagramNodeEndpoint, dvt.Container, 'DvtAdfDiagramNodeEndpoint');

DvtAdfDiagramNodeEndpoint.NODE_TYPE = 'top';


/**
 * Initialization method called by the constructor
 * @param {dvt.Context} context Platform specific context object
 * @param {dvt.AdfDiagramNode} parent Parent node
 * @param {string} id Optional id for this object
 * @param {object} styleMap The object containing style specifications for this component
 */
DvtAdfDiagramNodeEndpoint.prototype.Init = function(context, parent, id, styleMap)
{
  DvtAdfDiagramNodeEndpoint.superclass.Init.call(this, context, null, id + '_' + DvtAdfDiagramNodeEndpoint.NODE_TYPE + 'Hdl');
  this._type = DvtAdfDiagramNodeEndpoint.NODE_TYPE;
  this._shape = DvtAdfDiagramEndpointUtils.createEndpointShape(this.getCtx(), this._type, id, styleMap);
  this._endpointOffset = DvtAdfDiagramEndpointUtils.getEndpointOffset(styleMap);
  this._bLocked = false;
  this._bHovered = false;
  this.addChild(this._shape);

  //Fix for BUG 21148491: the diagram event manager uses mousedown and mouseup events to create or modify a diagram link.
  //The dvt.Button prevents the mousedown and mouseup events from bubling to the parent, so the diagram can't react on those events.
  //The separate listeners are added to the link endpoint button on capture phase in order to recieve and process the events.
  var eventManager = parent.GetDiagram().getEventManager();
  this._shape.addEvtListener(dvt.MouseEvent.MOUSEDOWN, eventManager.OnMouseDown, true, eventManager);
  this._shape.addEvtListener(dvt.MouseEvent.MOUSEUP, eventManager.OnMouseUp, true, eventManager);
  eventManager.addRolloverType(DvtAdfDiagramNodeEndpoint);
  eventManager.associate(this, this);
};

DvtAdfDiagramNodeEndpoint.prototype.setPosition = function(point) 
{
  this.setTranslate(point.x - this._endpointOffset, point.y - this._endpointOffset);
};

DvtAdfDiagramNodeEndpoint.prototype.getLayoutBounds = function() {
  return this.getDimensions();
};


/**
 * Sets visibility of the node endpoint. The visibility will not change if the object is locked
 * or if the object is in hover state
 * @param {boolean} bVisible True to make an object visible
 */
DvtAdfDiagramNodeEndpoint.prototype.setVisible = function(bVisible) {
  if ((this.isHovered() && !bVisible) || this.isLocked())
    return;

  this._shape.setVisible(bVisible);
};

DvtAdfDiagramNodeEndpoint.prototype.isLocked = function() {
  return this._bLocked;
};

DvtAdfDiagramNodeEndpoint.prototype.setLocked = function(bLocked) {
  this._bLocked = bLocked;
};


/**
 * @override
 */
DvtAdfDiagramNodeEndpoint.prototype.isSelectable = function() {
  return this.getParent().isSelectable();
};

/**
 * @override
 */
DvtAdfDiagramNodeEndpoint.prototype.setSelected = function(selected) {
  return this.getParent().setSelected(selected);
};

/**
 * @override
 */
DvtAdfDiagramNodeEndpoint.prototype.isSelected = function() {
  return this.getParent().isSelected();
};

DvtAdfDiagramNodeEndpoint.prototype.hideHoverEffect = function() {
  this.getParent().hideHoverEffect();
};


/**
 * Handler for rollout event
 * @param {dvt.MouseEvent|dvt.ComponentTouchEvent} event Trigger event
 */
DvtAdfDiagramNodeEndpoint.prototype.showRollOverEffect = function(event) {
  this.setHovered(true);
};


/**
 * Handler for rollover event
 * @param {dvt.MouseEvent|dvt.ComponentTouchEvent} event Trigger event
 */
DvtAdfDiagramNodeEndpoint.prototype.hideRollOverEffect = function(event) {
  this.setHovered(false);
};


/**
 * Returns true if the object is in hover state
 * @return {boolean} true if the object is in hover state
 */
DvtAdfDiagramNodeEndpoint.prototype.isHovered = function() {
  return this._bHovered;
};


/**
 * Sets and updates hover indicator for the object
 * @param {boolean} bHovered True if the object is in hover state
 */
DvtAdfDiagramNodeEndpoint.prototype.setHovered = function(bHovered) {
  this._bHovered = bHovered;
};
// Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
/**
 * If this object supports drag, returns the client id of the drag component.
 * Otherwise returns null.
 * @param mouseX the x coordinate of the mouse
 * @param mouseY the x coordinate of the mouse
 * @param clientIds the array of client ids of the valid drag components
 */
dvt.AdfDiagram.prototype.isDragAvailable = function(mouseX, mouseY, clientIds) {
  this._dragAllowed = false;
  return this._dragSource.isDragAvailable(clientIds);
};


/**
 * Returns the transferable object for a drag initiated at these coordinates.
 */
dvt.AdfDiagram.prototype.getDragTransferable = function(mouseX, mouseY) {
  return this._dragSource.getDragTransferable(mouseX, mouseY);
};


/**
 * Returns the feedback for the drag operation.
 */
dvt.AdfDiagram.prototype.getDragOverFeedback = function(mouseX, mouseY) {
  return this._dragSource.getDragOverFeedback(mouseX, mouseY);
};


/**
 * Returns an Object containing the drag context info.
 */
dvt.AdfDiagram.prototype.getDragContext = function(mouseX, mouseY) {
  return this._dragSource.getDragContext(mouseX, mouseY);
};


/**
 * Returns the offset to use for the drag feedback. This positions the drag
 * feedback relative to the pointer.
 */
dvt.AdfDiagram.prototype.getDragOffset = function(mouseX, mouseY) {
  return this._dragSource.getDragOffset(mouseX, mouseY);
};


/**
 * Returns the offset from the mouse pointer where the drag is considered to be located.
 */
dvt.AdfDiagram.prototype.getPointerOffset = function(xOffset, yOffset) {
  return this._dragSource.getPointerOffset(xOffset, yOffset);
};


/**
 * Notifies the component that a drag started.
 */
dvt.AdfDiagram.prototype.initiateDrag = function() {
  this._dragSource.initiateDrag();
  if (dvt.Agent.isPlatformIE() && dvt.Agent.getVersion() <= 10) {
    // Fix for the bug 18977231 - ie9/10 initiate dnd then release, diagram snaps to mouse position
    this.getPanZoomCanvas().panZoomEnd();
  }
};


/**
 * Clean up after the drag is completed.
 */
dvt.AdfDiagram.prototype.dragDropEnd = function() {
  this._dragSource.dragDropEnd();
};


/**
 * Returns true if the component allows drag. The method is called by the peer.
 * @return {boolean} Returns true if the component allows drag
 */
dvt.AdfDiagram.prototype.recognizeDrag = function() {
  return this._dragAllowed;
};


/**
 * Prepares for the drag, called by the peer
 */
dvt.AdfDiagram.prototype.prepDrag = function() {
  this._dragAllowed = true;
};
/**
 * If a drop is possible at these mouse coordinates, returns the client id
 * of the drop component. Returns null if drop is not possible.
 * @param {number} mouseX x coordinate
 * @param {number} mouseY y coordinate
 * @param {object|array} clientIds object that contains client ids for the potential drop targets
 * @param {function} flavorAcceptanceFunc flavor acceptance function, called to check drag source drop target flavors compatibility
 * @return {string} client id of the drop component or null
 */
dvt.AdfDiagram.prototype.acceptDrag = function(mouseX, mouseY, clientIds, flavorAcceptanceFunc) {
  return this._dropTarget.acceptDrag(mouseX, mouseY, clientIds, flavorAcceptanceFunc);
};


/**
 * Paints drop site feedback as a drag enters the drop site.
 */
dvt.AdfDiagram.prototype.dragEnter = function() {
  this._dropTarget.dragEnter();
};

/**
 * Called if the component has accepted a drag at the current location with the valid
 * actions.  This can be used as a hook for painting drop site feedback.
 */
dvt.AdfDiagram.prototype.dragAccepted = function() {
  this._dropTarget.dragAccepted();
};

/**
 * Cleans up drop site feedback as a drag exits the drop site.
 */
dvt.AdfDiagram.prototype.dragExit = function() {
  this._dropTarget.dragExit();
};


/**
 * Returns the object representing the drop site. This method is called when a valid
 * drop is performed.
 * @param {number} mouseX x coordinate
 * @param {number} mouseY y coordinate
 * @return {object} the object representing the drop site
 */
dvt.AdfDiagram.prototype.getDropSite = function(mouseX, mouseY) {
  return this._dropTarget.getDropSite(mouseX, mouseY);
};
/**
 * Drag source event handler.
 * @param {dvt.Context} context
 * @class DvtAdfDiagramDragSource
 * @constructor
 */
var DvtAdfDiagramDragSource = function(context, view) {
  this.Init(context, view);
};

dvt.Obj.createSubclass(DvtAdfDiagramDragSource, dvt.DragSource, 'DvtAdfDiagramDragSource');


/**
 * @param {dvt.Container} container
 * @param {dvt.Context} context
 */
DvtAdfDiagramDragSource.prototype.Init = function(context, view) {
  DvtAdfDiagramDragSource.superclass.Init.call(this, context);

  this._view = view;
};


/**
 * If there are any drag candidate, returns the client id of the drag component.
 * Otherwise returns null.
 */
DvtAdfDiagramDragSource.prototype.isDragAvailable = function(clientIds) {
  var ret = DvtAdfDiagramDragSource.superclass.isDragAvailable.call(this, clientIds);
  //Bug 13350809: prevent the hover window from appearing during DnD
  if (ret) {
    this._view.setPreventNodeDetailWindow(true);
  }
  return ret;
};


/**
 * Clean up after the drag is completed.
 */
DvtAdfDiagramDragSource.prototype.dragDropEnd = function() {
  DvtAdfDiagramDragSource.superclass.dragDropEnd.call(this);

  //Bug 13350809: re-enable the hover window after DnD
  this._view.setPreventNodeDetailWindow(false);
  this._view.getPanZoomCanvas().resetTouchTargets();
  if (this._view.IsPanningEnabled()) {
    this._view.getPanZoomCanvas().setPanningEnabled(true);
  }
};
// Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
/**
 * @constructor
 * Creates a drop target object for the component
 * @param {dvt.AdfDiagram|dvt.HierarchyViewer} view a component that created the drop target
 */
var DvtAdfDiagramDropTarget = function(view) {
  this.Init(view);
};

dvt.Obj.createSubclass(DvtAdfDiagramDropTarget, dvt.DropTarget, 'DvtAdfDiagramDropTarget');


/**
 * size of the zone around the edge of the view in which auto-panning will occur
 * @const
 * @private
 */
DvtAdfDiagramDropTarget._AUTO_PAN_ZONE_SIZE = 40;


/**
 * interval between ticks of the auto-pan timer
 * @const
 * @private
 */
DvtAdfDiagramDropTarget._AUTO_PAN_TIMER_INTERVAL = 50; //in ms


/**
 * interval between ticks of the check-auto-pan timer
 * @const
 * @private
 */
DvtAdfDiagramDropTarget._CHECK_AUTO_PAN_TIMER_INTERVAL = 300; //in ms


/**
 * Initializes the drop target object for the component
 * @param {dvt.AdfDiagram|dvt.HierarchyViewer} view a component that created the drop target
 * @protected
 */
DvtAdfDiagramDropTarget.prototype.Init = function(view) {
  this._view = view;

  //ER 13115332: auto-pan the view when dragging near the edge
  //center point of the view
  this._centerPoint = null;
  //mouse point of the drag
  this._mousePoint = null;
  //timer used for auto-panning
  this._autoPanTimer = null;
  //Bug 18382299: timer to check auto-panning - in case when the drag object is coming from external source we don't need to autopan immediately
  this._checkAutoPanTimer = new dvt.Timer(this._view.getCtx(), DvtAdfDiagramDropTarget._CHECK_AUTO_PAN_TIMER_INTERVAL,
      this.CheckAutoPan, this);
  //Bug 13115332: save the mouse coords passed to acceptDrag
  //for later use in dragAccepted
  this._acceptDragMouseX = 0;
  this._acceptDragMouseY = 0;
};

/**
 * @override
 */
DvtAdfDiagramDropTarget.prototype.acceptDrag = function(mouseX, mouseY, clientIds, flavorAcceptanceFunc) {
  // If there is no node under the point, then don't accept the drag
  var node = this._view.__getNodeUnderPoint(mouseX, mouseY);
  var currentTarget = null;
  if (node) {
    currentTarget = node;
  } else {
    currentTarget = null;
  }

  //verify drag-drop flavor compatibility, accept all external sources that were already verified by the framework
  //if current turget is the diagram itself
  if (flavorAcceptanceFunc && !flavorAcceptanceFunc(node ? node.DropFlavors : this._view.DropFlavors, !currentTarget))
    return null;

  if (currentTarget != this._dropSite) {
    this._dropSite = currentTarget;
  }

  //Bug 13115332: save the mouse coords here, but wait until dragAccepted is called
  //before actually trying to autopan.  The reason is that acceptDrag is always called,
  //even if the component will not accept the drop, but dragAccepted is only called
  //when the drop may actually be accepted.
  this._acceptDragMouseX = mouseX;
  this._acceptDragMouseY = mouseY;

  // If client ids is an array (HV case) return first id, since HV can only have single drop target
  if (dvt.ArrayUtils.isArray(clientIds)) {
    return clientIds[0];
  }
  //Diagram case - client ids is an object, return client id for the diagram or a node
  if (node)
    return clientIds['nodes'];
  else
    return clientIds['diagram'];
};

DvtAdfDiagramDropTarget.prototype.dragAccepted = function() {
  this._view.__showDropSiteFeedback(this._dropSite);

  //ER 13115332: check whether we need to start or stop auto-panning
  //wait until dragAccepted is called
  //before actually trying to autopan.  The reason is that acceptDrag is always called,
  //even if the component will not accept the drop, but dragAccepted is only called
  //when the drop may actually be accepted.
  if (!this._checkAutoPanTimer.isRunning()) {
    this._checkAutoPanTimer.start();
  }
};


/**
 * @override
 */
DvtAdfDiagramDropTarget.prototype.dragEnter = function() {
  //Bug 13350809: prevent the hover window from appearing during DnD
  this._view.setPreventNodeDetailWindow(true);
};


/**
 * @override
 */
DvtAdfDiagramDropTarget.prototype.dragExit = function() {

  if (this._checkAutoPanTimer.isRunning()) {
    this._checkAutoPanTimer.stop();
  }

  //Bug 13115332: stop auto-panning when the drag exits the view
  this.StopAutoPan();

  // Remove drop site feedback
  this._view.__removeDropSiteFeedback();
  this._dropSite = null;

  //Bug 13350809: re-enable the hover window after DnD
  this._view.setPreventNodeDetailWindow(false);
};


/**
 * @override
 */
DvtAdfDiagramDropTarget.prototype.getDropSite = function(mouseX, mouseY) {
  //Bug 13115332: stop auto-panning when a drop happens
  this.StopAutoPan();

  var node = this._view.__getNodeUnderPoint(mouseX, mouseY);
  if (node) {
    var rowKey = node.getData().getId();
    if (rowKey.substring(0, 1) == 'N') // strip 'N' identifier
      rowKey = rowKey.substring(1);
    return {clientRowKey: rowKey};
  }
  else
    return {};
};


/**
 * Check whether we need to start or stop auto-pan.
 */
DvtAdfDiagramDropTarget.prototype.CheckAutoPan = function() {
  var ww = this._view.getWidth();
  var hh = this._view.getHeight();
  var mouseX = this._acceptDragMouseX;
  var mouseY = this._acceptDragMouseY;
  var zoneSize = DvtAdfDiagramDropTarget._AUTO_PAN_ZONE_SIZE;
  var bAutoPan = (mouseX < zoneSize || mouseY < zoneSize ||
                  mouseX > ww - zoneSize || mouseY > hh - zoneSize);
  if (bAutoPan) {
    this.StartAutoPan(mouseX, mouseY);
  }
  else {
    this.StopAutoPan();
  }
};


/**
 * Start auto-pan.
 * @param mouseX the x coordinate of the mouse relative to the component
 * @param mouseY the x coordinate of the mouse relative to the component
 */
DvtAdfDiagramDropTarget.prototype.StartAutoPan = function(mouseX, mouseY) {
  this._mousePoint = new dvt.Point(mouseX, mouseY);
  if (!this._autoPanTimer) {
    var ww = this._view.getWidth();
    var hh = this._view.getHeight();
    this._centerPoint = new dvt.Point(.5 * ww, .5 * hh);

    this._autoPanTimer = new dvt.Timer(this._view.getCtx(), DvtAdfDiagramDropTarget._AUTO_PAN_TIMER_INTERVAL,
        this.HandleAutoPanTimer, this);
    this._autoPanTimer.start();
  }
};


/**
 * Stop auto-pan.
 */
DvtAdfDiagramDropTarget.prototype.StopAutoPan = function() {
  this._mousePoint = null;
  this._centerPoint = null;
  if (this._autoPanTimer) {
    this._autoPanTimer.stop();
    this._autoPanTimer = null;
  }
};


/**
 * Handle an event from the auto-pan timer.
 * @param event the timer event
 */
DvtAdfDiagramDropTarget.prototype.HandleAutoPanTimer = function(event) {
  //get the difference between the mouse position and the center of the view
  var diffY = this._centerPoint.y - this._mousePoint.y;
  var diffX = this._centerPoint.x - this._mousePoint.x;
  //calculate the slope and angle of the mouse from the center of the view
  //var slope = diffY / diffX;
  var angleRads = Math.atan2(diffY, diffX);
  //normalize dx and dy increments
  var deltaX = Math.cos(angleRads);
  var deltaY = Math.sin(angleRads);
  //multiply the normalized increment by this factor to determine
  //the pan distance
  var factor = 15;
  //pan the view
  this._view.getPanZoomCanvas().panBy(factor * deltaX, factor * deltaY);
};
// Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
/**
 * @constructor
 */
var DvtAdfDiagramEditorManager = function(context, diagram) {
  this._context = context;
  this._diagram = diagram;
  this._tempLinks = new Array();
  this._fakeLinks = new Array();
  this._fakeNodes = new Array();

  this._dragItems = new Array();
};

dvt.Obj.createSubclass(DvtAdfDiagramEditorManager, dvt.Obj, 'DvtAdfDiagramEditorManager');


/**
 * Handles drag start event.
 * @param {object} obj Logical object corresponding to the potential drag target
 * @param {dvt.Point} relPos Object coordinates relative to stage
 * @return {boolean} True if drag started
 */
DvtAdfDiagramEditorManager.prototype.processDragStart = function(obj, relPos) {
  var ret = new Object();
  var dragStarted = false;

  if (obj instanceof dvt.AdfDiagramNode)
    dragStarted = true;

  var result = this.processLinkUpdateStart(obj, relPos);
  dragStarted = dragStarted || result;

  if (this._diagram.isLinkToolEnabled()) {
    var resultCreate = this.processLinkCreateStart(obj, relPos);
    dragStarted = dragStarted || resultCreate;
  }
  if (this._diagram.isStickyNodeEnabled()) {
    this.processStickyNodeCreateStart(obj, relPos);
  }
  ret.dragStarted = dragStarted;
  return ret;
};

DvtAdfDiagramEditorManager.prototype.processDragMove = function(obj, relPos) {
  this.processLinkUpdateMove(obj, relPos);

  if (this._diagram.isLinkToolEnabled()) {
    this.processLinkCreateMove(obj, relPos);
  }
  if (this._diagram.isStickyNodeEnabled()) {
    this.processStickyNodeCreateMove(obj, relPos);
  }
};

DvtAdfDiagramEditorManager.prototype.processDragEnd = function(obj, relPos) {
  var events = new Array();

  this.processLinkUpdateEnd(obj, relPos, events);

  if (this._diagram.isLinkToolEnabled()) {
    this.processLinkCreateEnd(obj, relPos, events);
  }

  var ret = new Object();
  ret.events = events;
  return ret;
};

DvtAdfDiagramEditorManager.prototype.processDragCancel = function(obj, relPos) {
  var events = new Array();

  this.processLinkUpdateCancel(obj, relPos, events);

  if (this._diagram.isLinkToolEnabled()) {
    this.processLinkCreateCancel(obj, relPos, events);
  }

  var ret = new Object();
  ret.events = events;
  return ret;
};

DvtAdfDiagramEditorManager.prototype.processNodeCreate = function(node, relPos) {
  var events = new Array();
  if (this._diagram.isStickyNodeEnabled()) {
    this.processStickyNodeCreateEnd(node, relPos, events);
  }
  var ret = new Object();
  ret.events = events;
  return ret;
};

DvtAdfDiagramEditorManager.prototype.processLinkUpdateStart = function(obj, relPos) {
  if (obj && obj.isLinkEndpoint && obj.isLinkEndpoint() && obj.HandleChangeStart && obj.isUpdateEnabled()) {
    var pos = obj.stageToLocal(relPos);
    obj.HandleChangeStart(pos);
    this._dragItems.push(obj);
    return true;
  }
  return false;
};

DvtAdfDiagramEditorManager.prototype.processLinkUpdateMove = function(obj, relPos) {
  for (var i = 0; i < this._dragItems.length; i++) {
    var dragItem = this._dragItems[i];
    var pos = dragItem.stageToLocal(relPos);
    dragItem.HandleChangeMove(pos);
  }
};

/**
 * Update link on drag end
 * @param {object} obj  The logical object
 * @param {dvt.Point} relPos  relative position
 * @param {array} events  array of events
 */
DvtAdfDiagramEditorManager.prototype.processLinkUpdateEnd = function(obj, relPos, events) {
  for (var i = 0; i < this._dragItems.length; i++) {
    var dragItem = this._dragItems[i];
    if (obj) {
      var nNode = obj; // dragging into the node
      if (obj instanceof DvtAdfDiagramNodeEndpoint) // dragging into node endpoint
        nNode = obj.getParent();

      var link = dragItem.getLink();
      var linkData = link.getData();
      var pNode = this._diagram.getNodeById(linkData.getStartId());
      var cNode = this._diagram.getNodeById(linkData.getEndId());

      var oldEndpoint = null;
      var newEndpoint = null;
      var endpointType = null;

      var type = dragItem._type;
      if (type == DvtAdfDiagramLinkEndpoint.START_TYPE) {
        oldEndpoint = linkData.getStartId();
        linkData.setStartId(nNode.getId());
        pNode.removeOutLinkId(linkData.getId());
        nNode.addOutLinkId(linkData.getId());
        newEndpoint = linkData.getStartId();
        endpointType = 'source';
      } else {
        oldEndpoint = linkData.getEndId();
        linkData.setEndId(nNode.getId());
        cNode.removeInLinkId(linkData.getId());
        nNode.addInLinkId(linkData.getId());
        newEndpoint = linkData.getEndId();
        endpointType = 'destination';
      }

      var centerPoint = dvt.AdfDiagramEventManager.GetTempLinkPositionOnNode(obj, dragItem);
      dragItem.HandleChangeEndNewNode(centerPoint);

      events.push(new dvt.AdfDiagramChangeLinkEvent(link.getId(), oldEndpoint, newEndpoint, endpointType));

    } else {
      dragItem.HandleChangeEndRestore();
    }
  }
  this._dragItems = new Array();
};

DvtAdfDiagramEditorManager.prototype.processLinkUpdateCancel = function(node, relPos, events) {
  for (var i = 0; i < this._dragItems.length; i++) {
    var dragItem = this._dragItems[i];
    dragItem.HandleChangeEndRestore();
  }
  this._dragItems = new Array();
};


/**
 * Handles link creation start. Link creation can start when a user starts dragging from a node endpoint
 * @param {object} node Logical object corresponding to the drag target
 * @param {dvt.Point} relPos Object coordinates relative to stage
 * @return {boolean} True if this is a link creation drag
 */
DvtAdfDiagramEditorManager.prototype.processLinkCreateStart = function(node, relPos) {
  this._linkEndpoint = null;
  if (node instanceof DvtAdfDiagramNodeEndpoint) {
    if (node.setLocked)
      node.setLocked(true);
    this._linkEndpoint = node;
    return true;
  }
  else
    return false;
};


/**
 * Create a new potential link and show it if it is necessary.
 * The link will be shown when created with the mouse or touch movement. It will not be shown when the link is created with the keyboard.
 * @param {dvt.Point} relPos position of the link end
 */
DvtAdfDiagramEditorManager.prototype.showLinkCreateStart = function(relPos) {

  var node = this._linkEndpoint;
  var startId = node.getParent().getId();
  var linkData = new DvtAdfDiagramLinkData();
  linkData.setId('tempLink' + this._tempLinks.length);
  linkData.setStartId(startId);

  var createLinkData = this._diagram.getCreateLinkData();
  var linkDef = createLinkData.linkDef;

  linkData.setTemplateId(linkDef.getId());

  //Bug 14187300: pass diagram into link constructor
  var link = new dvt.AdfDiagramLink(this._context, linkData, this._diagram);
  link.setLinkDef(this._diagram.GetLinkDef(linkData.getTemplateId()));
  link.setMouseEnabled(false);
  link.paletteId =
      this._diagram.moveToDiagramFront(link);
  var startPos = dvt.AdfDiagramEventManager.GetTempLinkPositionOnNode(node, link);
  if (!relPos) {
    //link is created with keyboard, set the points as they needed for the link end, but do not render the link
    link.setPoints([startPos.x, startPos.y, startPos.x, startPos.y]);
  }
  else {
    var pos = link.stageToLocal(relPos);
    link.setPoints([startPos.x, startPos.y, pos.x, pos.y]);
    link.renderLink();
  }
  link.paletteId = createLinkData.paletteId;
  this._tempLinks.push(link);
};


/**
 * Handles drag move during link creation
 * @param {DvtAdfDiagramNodeEndpoint} obj logical object for the drag start
 * @param {dvt.Point} relPos position of the link end
 */
DvtAdfDiagramEditorManager.prototype.processLinkCreateMove = function(obj, relPos) {
  if (!this._linkEndpoint)
    return;
  this.showLinkCreateStart(relPos);
  for (var i = 0; i < this._tempLinks.length; i++) {
    var newLink = this._tempLinks[i];
    var pos = newLink.stageToLocal(relPos);
    var oldPoint = newLink.getPoints();
    newLink.setPoints([oldPoint[0], oldPoint[1], pos.x, pos.y]);
  }
};

/**
 * Process link on create end
 * @param {object} node  The logical object
 * @param {dvt.Point} relPos  relative position
 * @param {array} events  array of events
 */
DvtAdfDiagramEditorManager.prototype.processLinkCreateEnd = function(node, relPos, events) {
  if (this._linkEndpoint && this._linkEndpoint.setLocked) {
    this._linkEndpoint.setLocked(false);
    this._linkEndpoint.hideHoverEffect();
  }
  this._linkEndpoint = null;
  this.setLinkCreateWithKeyboard(false);
  var endId = null;
  if (node instanceof dvt.AdfDiagramNode)
    endId = node.getId();
  else if (node instanceof DvtAdfDiagramNodeEndpoint)
    endId = node.getParent().getId();

  for (var i = 0; i < this._tempLinks.length; i++) {
    var newLink = this._tempLinks[i];
    if (node && endId) {
      var linkData = newLink.getData();
      if (endId != linkData.getStartId()) {
        var realLinkId = 'fakeLink' + this._fakeLinks.length;

        linkData.setId(realLinkId);
        //linkData.setGroupId(node.getData().getGroupId());
        linkData.setEndId(endId);
        this._diagram.addLinkData(linkData);

        var realLink = this._diagram.getLinkById(realLinkId);
        realLink.setSelectable(false);
        var endPos = dvt.AdfDiagramEventManager.GetTempLinkPositionOnNode(node, realLink);
        var oldPoints = newLink.getPoints();
        var sourcePoint = new dvt.Point(oldPoints[0], oldPoints[1]);
        var stageSourcePoint = newLink.localToStage(sourcePoint);
        var localSourcePoint = realLink.stageToLocal(stageSourcePoint);
        realLink.setPoints([localSourcePoint.x, localSourcePoint.y, endPos.x, endPos.y]);
        realLink.renderLink();
        this._fakeLinks.push(realLink);
        // TODO: palette id
        events.push(new dvt.AdfDiagramCreateLinkEvent(linkData.getStartId(), linkData.getEndId(), newLink.paletteId));
      }
    }
    newLink.getParent().removeChild(newLink);
  }
  this._tempLinks = new Array();
};


/**
 * Handle link creation cancel event
 * @param {DvtAdfDiagramNodeEndpoint} node logical object for the drag start
 * @param {dvt.Point} relPos current position of the link end
 * @param {array} events array of link creation events
 */
DvtAdfDiagramEditorManager.prototype.processLinkCreateCancel = function(node, relPos, events) {
  if (this._linkEndpoint && this._linkEndpoint.setLocked) {
    this._linkEndpoint.setLocked(false);
    this._linkEndpoint.hideHoverEffect();
  }
  this._linkEndpoint = null;
  this.setLinkCreateWithKeyboard(false);
  for (var i = 0; i < this._tempLinks.length; i++) {
    var newLink = this._tempLinks[i];
    newLink.getParent().removeChild(newLink);
  }
  this._tempLinks = new Array();
};

DvtAdfDiagramEditorManager.prototype.processStickyNodeCreateStart = function(obj, relPos, events) {
  this._stickyNodeStarted = true;
};

DvtAdfDiagramEditorManager.prototype.processStickyNodeCreateMove = function(obj, relPos, events) {
  this._stickyNodeStarted = false;
};


/**
 * Create New Node in diagram editor
 * @param {dvt.Point} relPos  relative position of new node
 * @param {dvt.AdfDiagramNodeDef} nodeDef  Node definition
 * @param {string} parentId  parent id
 * @param {string} paletteId  palette id
 */
DvtAdfDiagramEditorManager.prototype.createNewNode = function(relPos, nodeDef, parentId, paletteId) 
{
  if (nodeDef) {
    var nodeData = new dvt.AdfDiagramNodeData();
    var nodeId = 'fakeNode' + this._fakeNodes.length;

    nodeData.setTemplateId(nodeDef.getId());

    nodeData.setId(nodeId);
    //nodeData.setGroupId(s);
    nodeData.setHasAnchorListener(false);
    this._diagram.addNodeData(nodeData);
    var newNode = this._diagram.getNodeById(nodeId);
    newNode.setSelectable(false);
    var pos = newNode.stageToLocal(relPos);

    var afContext = this._diagram.createAfContext();
    newNode.createNodeContents(afContext);

    newNode.renderNode(afContext);
    var nodeDim = newNode.getLayoutBounds();

    newNode.setTranslate(pos.x - nodeDim.w / 2, pos.y - nodeDim.h / 2);

    this._fakeNodes.push(newNode);
  }
  var evt = dvt.AdfDiagramEventManager.GetCreateNodeEvent(parentId, relPos, paletteId);
  return evt;
};


/**
 * Handles node creation event
 * @param {dvt.AdfDiagramNode} node optional logical object - a container for the new node
 * @param {dvt.Point} relPos a position of the new node
 * @param {array} events node creation events
 */
DvtAdfDiagramEditorManager.prototype.processStickyNodeCreateEnd = function(node, relPos, events) {
  if (this._stickyNodeStarted) {
    var parentId = null;
    if (node) {
      parentId = node.getId();
    }

    var nodeDef = this._diagram.getCreateNodeData();
    var evt = this.createNewNode(relPos, nodeDef.nodeDef, parentId, nodeDef.paletteId);
    events.push(evt);
    this._stickyNodeStarted = false;
  }
};


/**
 * Set link creation with keyboard flag
 * @param {boolean} bStart true if link creation with keyboard started
 */
DvtAdfDiagramEditorManager.prototype.setLinkCreateWithKeyboard = function(bStart) {
  this._linkCreateWithKeyboard = bStart;
};


/**
 * Get link creation with keyboard flag
 * @return {boolean} true if link creation with keyboard started
 */
DvtAdfDiagramEditorManager.prototype.getLinkCreateWithKeyboard = function() {
  return this._linkCreateWithKeyboard;
};


/**
 * Process link creation with keyboard
 * @param {dvt.AdfDiagramNode} node a node for a potential link end
 */
DvtAdfDiagramEditorManager.prototype.processLinkCreateWithKeyboard = function(node) {
  if (node && node.getLinkHandle()) {
    var linkHandle = node.getLinkHandle();
    var bounds = linkHandle.getLayoutBounds();
    var relPos = linkHandle.localToStage(new dvt.Point(bounds.w * .5, bounds.h * .5));
    this.processLinkCreateMove(null, relPos);
  }
  else {
    this.showLinkCreateStart(null);
  }
};


/**
 * Process node creation with keyboard
 * @param {dvt.AdfDiagramNode} node an optional container node
 */
DvtAdfDiagramEditorManager.prototype.processNodeCreateWithKeyboard = function(node) {
  var relPos = node ? node.localToStage(new dvt.Point(0, 0)) : this._diagram.localToStage(new dvt.Point(0, 0));
  this.processStickyNodeCreateStart(node, relPos);
  var ret = this.processNodeCreate(node, relPos);
  var editorEvents = ret.events;
  if (editorEvents.length > 0) {
    for (var i = 0; i < editorEvents.length; i++) {
      this._diagram.dispatchEvent(editorEvents[i]);
    }
  }
};
/**
 * @constructor
 */
dvt.AdfDiagramFetchEvent = function(subtype, node) {
  this.Init(dvt.AdfDiagramFetchEvent.TYPE);
  this._subtype = subtype;
  this._node = node;
};

dvt.Obj.createSubclass(dvt.AdfDiagramFetchEvent, dvt.BaseComponentEvent, 'dvt.AdfDiagramFetchEvent');

dvt.AdfDiagramFetchEvent.TYPE = 'diagramFetchEvent';

dvt.AdfDiagramFetchEvent.DISCLOSE = 'disclose';
dvt.AdfDiagramFetchEvent.DRILL = 'drill';


dvt.AdfDiagramFetchEvent.prototype.getNode = function() {
  return this._node;
};

dvt.AdfDiagramFetchEvent.prototype.getSubtype = function() {
  return this._subtype;
};
/**
 * Utility functions for converting between JSON and XML APIs.
 * @class
 */
var DvtAdfDiagramJsonUtils = function() {};

dvt.Obj.createSubclass(DvtAdfDiagramJsonUtils, dvt.Obj, 'DvtAdfDiagramJsonUtils');

//Diagram and HierachyViewer element names
DvtAdfDiagramJsonUtils.ELEM_NODE_TEMPLATES = 'nodeTemplates';
DvtAdfDiagramJsonUtils.ELEM_NODE_TEMPLATE_ID = 'templateId';
DvtAdfDiagramJsonUtils.ELEM_NODE_DATA = 'nodes';

DvtAdfDiagramJsonUtils.ELEM_LINK_TEMPLATES = 'linkTemplates';
DvtAdfDiagramJsonUtils.ELEM_LINK_TEMPLATE_ID = 'linkTemplateId';
DvtAdfDiagramJsonUtils.ELEM_LINK_DATA = 'links';

DvtAdfDiagramJsonUtils.ELEM_CUSTOM_ATTRIBUTES = 'attributes';

DvtAdfDiagramJsonUtils.ATTR_NODE_ID = 'id';
DvtAdfDiagramJsonUtils.ATTR_COMPONENT = 'component';
DvtAdfDiagramJsonUtils.ATTR_CHILDREN = 'children';

DvtAdfDiagramJsonUtils._maps = null;


/**
 * Converts the data JSON API into the XML API.
 * @param {string} compName The component name
 * @param {object} data The object containing data specifications for this component.
 * @return {string} The resulting XML string.
 */
DvtAdfDiagramJsonUtils.prototype.toXml = function(compName, data) {

  //data = {"id":"shv","styleclass":"AFStretchWidth","controlPanelBehavior":"initExpanded","displayLevelsChildren":"1",
  var ret = '';

  //write root element
  if (compName && data) {
    var root = 'r';
    ret += this.StartElement(root);
    ret += this.WriteComponent(compName, data);
    ret += this.EndElement(root);
  }

  return ret;
};


DvtAdfDiagramJsonUtils.prototype.WriteComponent = function(compName, data) {
  var ret = '';

  // Note: the order of child elements is important to the diagram
  // Ex: "nodeTemplates" and "linkTemplates" must be defined before "nodes"
  //     "nodes" must be defined before "links"
  // most browsers loop over the props of an object in the order they were defined
  // but not Rhino
  if (compName && data) {
    ret = '<' + compName;
    ret += this.WriteAttributes(data);
    ret += '>';

    ret += this.WriteTemplates(data);

    ret += this.WriteData(compName, data);

    ret += this._writeInternalData(data);

    ret += this.EndElement(compName);
  }

  return ret;
};


DvtAdfDiagramJsonUtils.prototype.WriteTemplates = function(data) {
  var ret = '';

  var t = dvt.AdfDiagramParser._ELEM_TEMPLATES;
  ret += this.StartElement(t);

  var nodeTemplates = DvtAdfDiagramJsonUtils.ELEM_NODE_TEMPLATES;
  if (data[nodeTemplates]) {
    ret += this.WriteNodeTemplates(dvt.AdfDiagramParser._ELEM_NODE, data[nodeTemplates]);
  }

  var linkTemplates = DvtAdfDiagramJsonUtils.ELEM_LINK_TEMPLATES;
  if (data[linkTemplates]) {
    ret += this.WriteLinkTemplates(dvt.AdfDiagramParser._ELEM_LINK, data[linkTemplates]);
  }

  ret += this.EndElement(t);

  return ret;
};


DvtAdfDiagramJsonUtils.prototype.WriteLinkTemplates = function(prop, value) {
  var ret = '';

  if (value instanceof Array) {
    for (var i = 0; i < value.length; i++) {
      ret += this.StartElement(prop, value[i]);
      ret += this.EndElement(prop);
    }
  }

  return ret;
};


DvtAdfDiagramJsonUtils.prototype.WriteNodeTemplates = function(prop, value) {
  var ret = '';

  if (value instanceof Array) {
    for (var i = 0; i < value.length; i++) {
      ret += this.StartElement(prop, value[i]);
      ret += this.WriteFacets(value[i]);
      ret += this.EndElement(prop);
    }
  }

  return ret;
};

DvtAdfDiagramJsonUtils.prototype.WriteFacets = function(data) {
  var ret = '';

  var f = dvt.AdfDiagramParser._ELEM_F;

  var facetNames = ['zoom100', 'zoom75', 'zoom50', 'zoom25', 'background',
                    'containerBottom', 'containerEnd', 'containerLeft', 'containerRight',
                    'containerStart', 'containerTop', 'label', 'overlay'];

  for (var i = 0; i < facetNames.length; i++) {
    var name = facetNames[i];
    var value = data[name];
    if (value) {
      ret += '<' + f;
      ret += this.WriteAttr('name', name);
      ret += '>';

      ret += this.WriteFacet(value);
      ret += this.EndElement(f);
    }
  }

  return ret;
};

DvtAdfDiagramJsonUtils.prototype.WriteFacet = function(data) {
  var ret = '';

  if (data instanceof Array) {
    for (var i = 0; i < data.length; i++) {
      ret += this.WriteAfComponent(data[i]);
    }
  }

  return ret;
};

DvtAdfDiagramJsonUtils.prototype.WriteAfComponent = function(data) {
  var ret = '';

  var jsonName = data[DvtAdfDiagramJsonUtils.ATTR_COMPONENT];
  if (jsonName) {

    var xmlName = DvtAdfDiagramJsonUtils._toXMLName(jsonName);
    ret = '<' + xmlName;
    ret += this.WriteAttributes(data, DvtAdfDiagramJsonUtils.ATTR_COMPONENT);
    ret += '>';

    var children = data[DvtAdfDiagramJsonUtils.ATTR_CHILDREN];
    if (children instanceof Array) {
      for (var i = 0; i < children.length; i++) {
        ret += this.WriteAfComponent(children[i]);
      }
    }

    ret += this.EndElement(xmlName);
  }

  return ret;
};


/** @protected **/
DvtAdfDiagramJsonUtils.prototype.StartElement = function(key, value) {
  var ret = '';
  if (key) {
    ret = '<' + key;

    if (value)
      ret += this.WriteAttributes(value);

    ret += '>';
  }

  return ret;
};


/** @protected **/
DvtAdfDiagramJsonUtils.prototype.EndElement = function(key) {
  var ret = '';
  if (key) {
    ret = '<\/' + key + '>';
  }
  return ret;
};


/** @protected **/
DvtAdfDiagramJsonUtils.prototype.WriteAttributes = function(data, exProp) {
  var ret = '';

  // Loop through all properties of data
  for (var prop in data) {
    var value = data[prop];

    if (! (value instanceof Array) &&
        ! (value instanceof Object)) {

      // Scalar Attrs
      if (prop != exProp)
        ret += this.WriteAttr(prop, value);
    }
  }

  return ret;
};


/** @protected **/
DvtAdfDiagramJsonUtils.prototype.WriteAttr = function(attrName, value) {
  //value need xml encoding
  //"<" and "&" are not valid in xml
  value = value.replace(/&/g, '&amp;');
  value = value.replace(/</g, '&lt;');
  value = value.replace(/>/g, '&gt;');

  return value ? ' ' + attrName + '=\"' + value + '\"' : '';
};


DvtAdfDiagramJsonUtils.prototype.WriteData = function(compName, data) {
  var ret = '';

  var d = dvt.AdfDiagramParser._ELEM_DATA;
  ret += this.StartElement(d);

  ret += this.WriteNodes(compName, data, true);

  //diagram may specify link data
  var linkData = data[DvtAdfDiagramJsonUtils.ELEM_LINK_DATA];
  if (linkData) {
    ret += this.WriteLinks(dvt.AdfDiagramParser._ELEM_LINK_DATA, linkData);
  }
  else if (DvtAdfDiagramJsonUtils._isHV(compName)) {
    ret += this.WriteHvLinks(data, '');
  }

  ret += this.EndElement(d);

  return ret;
};

DvtAdfDiagramJsonUtils.prototype.WriteNodes = function(compName, data, isRoot) {
  var ret = '';

  if (data) {
    var nodeData = DvtAdfDiagramJsonUtils.ELEM_NODE_DATA;
    var nodeList = data[nodeData];

    if (nodeList instanceof Array) {
      for (var i = 0; i < nodeList.length; i++) {
        var pcc = (i == 0) ? nodeList.length.toString() : '';
        ret += this.WriteNodeData(compName, dvt.AdfDiagramParser._ELEM_NODE_DATA,
                                  nodeList[i], i, pcc, isRoot);
      }
    }
  }
  return ret;
};


DvtAdfDiagramJsonUtils.prototype.WriteNodeData = function(compName, name, data, rowIndex, 
    parentChildrenCount, isRoot) {
  var ret = '';

  ret = '<' + name;

  //write node data attributes
  var prop = DvtAdfDiagramJsonUtils.ATTR_NODE_ID;
  ret += this.WriteAttr(dvt.AdfDiagramParser._ATTR_ID, data[prop]);

  prop = DvtAdfDiagramJsonUtils.ELEM_NODE_TEMPLATE_ID;
  if (data[prop])
    ret += this.WriteAttr(dvt.AdfDiagramParser._ATTR_TEMPLATE_ID, data[prop]);

  if (DvtAdfDiagramJsonUtils._isHV(compName)) {
    ret += this.WriteAttr(DvtHvParser._ATTR_ROW_INDEX_IN_PARENT, rowIndex.toString());

    if (parentChildrenCount) {
      ret += this.WriteAttr(DvtHvParser._ATTR_PARENT_CHILD_COUNT, parentChildrenCount);
    }

    if (isRoot) {
      ret += this.WriteAttr(DvtHvParser._ATTR_ROOT, DvtHvParser._BOOLEAN_VALUE_TRUE);

      //to set focus
      if (rowIndex == 0) {
        ret += this.WriteAttr('ni', 'realRoot');
      }

      //disable setAnchor and navigateUp for now
      ret += this.WriteAttr(DvtHvParser._ATTR_SHOW_NAVIGATE_UP, DvtHvParser._BOOLEAN_VALUE_FALSE);
    }

    ret += this.WriteAttr(DvtHvParser._ATTR_SHOW_SET_ANCHOR, DvtHvParser._BOOLEAN_VALUE_FALSE);
  }
  else {
    ret += this.WriteAttr(dvt.AdfDiagramParser._ATTR_ROW_INDEX, rowIndex.toString());
  }

  ret += '>';

  //write node EL bindings
  ret += this.WriteELBindings(data);

  ret += this.EndElement(name);

  ret += this.WriteNodes(compName, data, false);

  return ret;
};

DvtAdfDiagramJsonUtils.prototype.WriteELBindings = function(data) {
  var ret = '';

  var el = dvt.AdfDiagramParser._ELEM_EL;
  var attributes = DvtAdfDiagramJsonUtils.ELEM_CUSTOM_ATTRIBUTES;
  var bindings = data[attributes];

  if (bindings) {
    ret += this.StartElement(el, bindings);
    ret += this.EndElement(el);
  }

  return ret;
};

DvtAdfDiagramJsonUtils.prototype.WriteHvLinks = function(data, parentTemplateId) {
  var ret = '';

  if (data) {
    var nodeData = DvtAdfDiagramJsonUtils.ELEM_NODE_DATA;
    var nodeList = data[nodeData];

    if (nodeList instanceof Array) {
      for (var i = 0; i < nodeList.length; i++) {
        ret += this.WriteHvLinkData(dvt.AdfDiagramParser._ELEM_LINK_DATA, nodeList[i], parentTemplateId);
      }
    }
  }
  return ret;
};

DvtAdfDiagramJsonUtils.prototype.WriteHvLinkData = function(name, data, parentTemplateId) {
  var ret = '';
  var templateId = data[DvtAdfDiagramJsonUtils.ATTR_NODE_ID];

  //no link data for the root nodes
  if (parentTemplateId) {
    ret = '<' + name;

    //write link data attributes
    var prop = DvtAdfDiagramJsonUtils.ELEM_LINK_TEMPLATE_ID;
    if (data[prop])
      ret += this.WriteAttr(dvt.AdfDiagramParser._ATTR_TEMPLATE_ID, data[prop]);

    ret += this.WriteAttr(dvt.AdfDiagramParser._ATTR_CHILD_ID, templateId);

    ret += this.WriteAttr(dvt.AdfDiagramParser._ATTR_PARENT_ID, parentTemplateId);

    ret += '>';

    ret += this.EndElement(name);
  }

  ret += this.WriteHvLinks(data, templateId);

  return ret;
};

DvtAdfDiagramJsonUtils.prototype._writeInternalData = function(data) {
  var ret = '';

  var resources = [dvt.AdfDiagramParser._ELEM_RESOURCES, dvt.AdfDiagramParser._ELEM_SELECTORS, dvt.AdfDiagramParser._ELEM_AF_STYLES];

  for (var i = 0; i < resources.length; i++) {
    var key = resources[i];
    var value = data[key];
    if (value)
      ret += this.WriteObject(key, value);
  }

  return ret;
};

DvtAdfDiagramJsonUtils.prototype.WriteLinks = function(name, data) {
  var ret = '';

  if (data instanceof Array) {
    for (var i = 0; i < data.length; i++) {
      ret += this.StartElement(name, data[i]);
      //write node EL bindings
      ret += this.WriteELBindings(data[i]);
      ret += this.EndElement(name);
    }
  }

  return ret;
};

DvtAdfDiagramJsonUtils.prototype.WriteObject = function(name, value) {
  var ret = '';

  if (name && value) {
    ret += '<' + name;
    ret += this.WriteAttributes(value);
    ret += '>';
    ret += this.WriteChildObjects(value);
  }

  ret += this.EndElement(name);

  return ret;
};

DvtAdfDiagramJsonUtils.prototype.WriteChildObjects = function(data) {
  var ret = '';

  for (var prop in data) {
    var value = data[prop];

    //child element
    if ((value instanceof Object)) {
      ret += this.WriteObject(prop, value);
    }
  }

  return ret;
};

DvtAdfDiagramJsonUtils._isHV = function(compName) {
  return (compName == 'hv');
};

DvtAdfDiagramJsonUtils._toXMLName = function(jsonName) {
  if (!DvtAdfDiagramJsonUtils._maps) {
    DvtAdfDiagramJsonUtils._maps = {
      'button' : dvt.AfButton.TAG_NAME,
      'clientBehavior' : dvt.AfClientBehavior.TAG_NAME,
      'commandButton' : dvt.AfCommandButton.TAG_NAME,
      'commandImageLink' : dvt.AfCommandImageLink.TAG_NAME,
      'commandLink' : dvt.AfCommandLink.TAG_NAME,
      'commandMenuItem' : dvt.AfCommandMenuItem.TAG_NAME,
      'goButton' : dvt.AfGoButton.TAG_NAME,
      'goLink' : dvt.AfGoLink.TAG_NAME,
      'group' : dvt.AfGroup.TAG_NAME,
      'image' : dvt.AfImage.TAG_NAME,
      'link' : dvt.AfLink.TAG_NAME,
      'marker' : dvt.AfMarker.TAG_NAME,
      'menu' : dvt.AfMenu.TAG_NAME,
      'outputFormatted' : dvt.AfOutputFormatted.TAG_NAME,
      'outputText' : dvt.AfOutputText.TAG_NAME,
      'panelCard' : dvt.AfPanelCard.TAG_NAME,
      'panelFormLayout' : dvt.AfPanelFormLayout.TAG_NAME,
      'panelGroupLayout' : dvt.AfPanelGroupLayout.TAG_NAME,
      'panelLabelAndMessage' : dvt.AfPanelLabelAndMessage.TAG_NAME,
      'pointLocation' : dvt.AfPointLocation.TAG_NAME,
      'separator' : dvt.AfSeparator.TAG_NAME,
      'showDetailItem' : dvt.AfShowDetailItem.TAG_NAME,
      'showPopupBehavior' : dvt.AfShowPopupBehavior.TAG_NAME,
      'spacer' : dvt.AfSpacer.TAG_NAME
    };
  }

  return DvtAdfDiagramJsonUtils._maps[jsonName];
};


/**
 * Quick check to see if the input string starts with a "<"
 * @return {boolean}
 */
DvtAdfDiagramJsonUtils.isXmlStartChar = function(str) {

  // make sure it's a string
  if (str.charAt && str.replace) {
    //remove leading whitespaces
    var s = dvt.StringUtils.trim(str);
    if (s.charAt(0) == '<') {
      return true;
    }
  }
  return false;
};

//// JSON Parsing Functions ////
/**
 * DvtDiagramPoint is exposed to public in AdfDiagram layout code.
 */
window['DvtDiagramPoint'] = dvt.DiagramPoint;
/**
 * DvtDiagramRectangle is exposed to public in AdfDiagram layout code.
 */
window['DvtDiagramRectangle'] = dvt.DiagramRectangle;
dvt.exportProperty(dvt, 'AdfDiagram', dvt.AdfDiagram);
/**
 * @constructor
 * Creates Palette UI control
 * @param {dvt.Context} Context object
 * @param {string} id Component id
 * @param {number} ww Max content width
 * @param {number} hh Max content height
 * @param {function} paletteHandler Palette events handler
 * @param {dvt.AdfDiagram} paletteHandlerObj Parent diagram component
 * @param {images} images for the control
 * @param {Object} styleMap for the control
 */
dvt.Palette = function(context, id, ww, hh, paletteHandler, paletteHandlerObj, dragSource, images, styleMap)
{
  this.Init(context, id, ww, hh, paletteHandler, paletteHandlerObj, dragSource, images, styleMap);
};

dvt.Obj.createSubclass(dvt.Palette, dvt.Container, 'dvt.Palette');

//Only Export the constants
dvt.Palette.PALETTE_DISCLOSED_KEY = 'isPaletteDisclosed';
dvt.Palette.PALETTE_ACTIVE_LINK_KEY = 'paletteActiveLinkInfo';
dvt.Palette.PALETTE_ACTIVE_SECTIONS_KEY = 'paletteActiveSectionsInfo';


/**
 * @protected
 * Initialization method called by the constructor
 * @param {dvt.Context} Context object
 * @param {string} id Component id
 * @param {number} ww Max content width
 * @param {number} hh Max content height
 * @param {function} paletteHandler Palette events handler
 * @param {dvt.AdfDiagram} paletteHandlerObj Parent diagram component
 * @param {images} images for the control
 * @param {Object} styleMap for the control
 */
dvt.Palette.prototype.Init = function(context, id, ww, hh, paletteHandler, paletteHandlerObj, dragSource, images, styleMap) {
  dvt.Palette.superclass.Init.call(this, context);
  this._paletteHandler = paletteHandler;
  this._diagram = paletteHandlerObj;
  this._dragSource = dragSource;
  this._parent = null;
  this._sectionContent = [];
  this._bDisclosed = true;
  this._activeSections = [];
  var defaultStyles = new DvtPaletteDefaults();
  this._styleMap = defaultStyles.calcOptions(styleMap);

  this._eventManager = new DvtPaletteEventManager(context, this._paletteHandler, this._diagram, this);
  if (!dvt.Agent.isTouchDevice())
    this._eventManager.setKeyboardHandler(this.CreateKeyboardHandler(this._eventManager));
  this._eventManager.addListeners(this);
  this._eventManager.associate(this, this);

  this._accordion = new dvt.Accordion(context, id, ww, hh, this._eventManager, images, this._styleMap);
  this._accordion.setCollapseAll(true);
  this._accordion.setExpandMany(true);
  this._accordion.addEvtListener(dvt.ResizeEvent.RESIZE_EVENT, this._handleResize, false, this);
};

dvt.Palette.prototype.getDiagram = function() {
  return this._diagram;
};

dvt.Palette.prototype.getEventManager = function() {
  return this._eventManager;
};

/**
 * Set the JSON object in dvt.Palette
 * @param {object} jsonObj  JSON object
 * @public
 */
dvt.Palette.prototype.setJSON = function(jsonObj) {
  //Parse palette JSON to dvt.Palette
  if (jsonObj) {
    this._stickyPaletteData = [];
    if (jsonObj) {
      this._bDisclosed = jsonObj['disclosed'] == 'false' ? false : true;
      this.setNoWrap(jsonObj['noWrap']);

      var maxWidthFromTag = this._getMaxWidthValue(jsonObj['maxWidth']);
      this._accordion.setMaxWidth(Math.min(maxWidthFromTag ? maxWidthFromTag : this._styleMap['maxWidth'], this._accordion.getMaxWidth()));
      this._accordion.setMinWidth(Math.min(this._styleMap['minWidth'], this._accordion.getMaxWidth()));

      this._parsePaletteSectionJSON(jsonObj['paletteSection']);
      this._parseLinkSectionJSON(jsonObj['paletteLinkSection']);
    }
  }
};

/**
 * @private
 * Helper methods that parses and creates a palette item
 * @param {object} items  JSON object to parse
 * @param {DvtPaletteSection} sectionObj  Palette section that hosts the item
 * @param {boolean} isLink True if the item is a link
 */
dvt.Palette.prototype._parsePaletteItemJSON = function(items, sectionObj, isLink) {
  for (var i = 0; i < items.length; i++) {
    var paletteItem = items[i];
    var id = paletteItem['id'];
    var childUIComponents = paletteItem['children'];
    var stamp, stickyItemData;

    if (isLink) {
      stickyItemData = new DvtPaletteLinkItem(this._context, this, paletteItem['typeHint'], id, paletteItem['label'], null, this._styleMap);
      stamp = this.getDiagram().GetJsonParser().parseLinkTemplate(childUIComponents ? childUIComponents[0] : null);
    }
    else {
      stickyItemData = new DvtPaletteStickyItem(this._context, this, paletteItem['typeHint'], id, paletteItem['label'], paletteItem['dragFlavor'], this._styleMap);
      stamp = dvt.AfComponentFactory.parseJson(childUIComponents);
    }
    if (!this._stampDefs) {
      this._stampDefs = new Object();
    }
    this._stampDefs[id] = stamp;
    sectionObj.addItem(stickyItemData);
    this._stickyPaletteData.push(stickyItemData);
  }
};


/**
 * Parses palette link section from JSON object
 * @param {object} paletteLinkSections  The JSON object to parse
 * @private
 */
dvt.Palette.prototype._parseLinkSectionJSON = function(paletteLinkSections) {
  if (paletteLinkSections) {
    for (var i = 0; i < paletteLinkSections.length; i++) {
      var paletteLink = paletteLinkSections[i];
      var linkSection = new DvtPaletteSection(this, paletteLink['id'], paletteLink['label'], this._styleMap);
      linkSection.setDisclosed(paletteLink['disclosed']);
      linkSection.setSelectedItemId(paletteLink['selected']);
      this._linkSection = linkSection;
      this._parsePaletteItemJSON(paletteLink['paletteItem'], linkSection, true);
    }
  }
};


/**
 * Parses palette node section
 * @param {object} paletteSections  The JSON object to parse
 * @private
 */
dvt.Palette.prototype._parsePaletteSectionJSON = function(paletteSections) {
  if (paletteSections) {
    for (var i = 0; i < paletteSections.length; i++) {
      var paletteSection = paletteSections[i];
      var sectionObj = new DvtPaletteSection(this, paletteSection['id'], paletteSection['label'], this._styleMap);
      sectionObj.setDisclosed(paletteSection['disclosed']);
      this.addSectionContent(sectionObj);
      this._parsePaletteItemJSON(paletteSection['paletteItem'], sectionObj, false);
    }
  }
};

/**
 * Refires the resize event to the palette's listeners after redrawing the scrolling container
 * @param {dvt.ResizeEvent} event The resize event
 * @private
 */
dvt.Palette.prototype._handleResize = function(event) {
  if (this._scrollingContainer) {
    this.removeChild(this._scrollingContainer);
    this._scrollingContainer = new dvt.SimpleScrollableContainer(this.getCtx(), Math.min(this._accordion.getMaxWidth(), event.getWidth()), Math.min(this._accordion.getMaxHeight(), event.getHeight()));
    this.addChild(this._scrollingContainer);
    this._scrollingContainer.getScrollingPane().addChild(this._accordion);
    this._scrollingContainer.prepareContentPane();
    this.FireListener(event);
  }
};

dvt.Palette.prototype.addSectionContent = function(section)
{
  this._sectionContent.push(section);
};


/**
 * Renders palette component
 */
dvt.Palette.prototype.render = function() {

  // create accordion sections
  if (this.getDiagram().isNodeCreateEnabled()) {
    for (var i = 0; i < this._sectionContent.length; i++) {
      //this._renderSeparator(this._container);
      var sectionContent = new dvt.Container(this.getCtx());
      var section = this._sectionContent[i];
      var label = section.getLabel();
      var sectionId = section.getId();
      var bActive = section.isDisclosed();
      this.addChild(sectionContent); //temporary add to the DOM in order to render
      section.render(sectionContent, this._accordion.getMaxWidth(), this._accordion.getMaxHeight());
      this.removeChild(sectionContent);
      this._accordion.addSection(label, sectionContent, bActive, true, sectionId);
      if (bActive)
        this._activeSections.push(sectionId);
    }
  }

  if (this._linkSection) {
    if (this.getDiagram().isLinkCreateEnabled()) {
      var sectionContent = new dvt.Container(this.getCtx());
      var label = this._linkSection.getLabel();
      var sectionId = this._linkSection.getId();
      var bActive = this._linkSection.isDisclosed();
      this.addChild(sectionContent); //temporary add to the DOM in order to render
      this._linkSection.render(sectionContent, this._accordion.getMaxWidth(), this._accordion.getMaxHeight());
      this.removeChild(sectionContent);
      this._accordion.addSection(label, sectionContent, bActive, true, sectionId);
      if (bActive)
        this._activeSections.push(sectionId);
    }
  }

  this.addChild(this._accordion); // temporary until we know its dimensions so we can create a scrolling container
  this._accordion.render();
  var dims = this._accordion.getDimensionsWithStroke();
  this._scrollingContainer = new dvt.SimpleScrollableContainer(this.getCtx(), Math.min(this._accordion.getMaxWidth(), dims.w), Math.min(this._accordion.getMaxHeight(), dims.h));
  this.addChild(this._scrollingContainer);
  this._scrollingContainer.getScrollingPane().addChild(this._accordion);
  this._scrollingContainer.prepareContentPane();

  this.getEventManager().setDragSource(this._dragSource);
  for (var i = 0; i < this._stickyPaletteData.length; i++) {
    var data = this._stickyPaletteData[i];
    data.AdjustItemDimensions(this._accordion.getMaxSectionWidth());
    this.getEventManager().associate(data.getDisplayableContainer(), data);
  }

  if (this._linkSection) {
    var items = this._linkSection.getItems();
    if (items && items.length > 0) {
      var selectedItem = this._linkSection.getSelectedItem();
      if (selectedItem)
        this.getEventManager().ProcessSelectionEventHelper(selectedItem, false);
    }
  }
};


dvt.Palette.prototype.isDisclosed = function()
{
  return this._bDisclosed;
};


/*---------------------------------------------------------------------*/
/*   setNoWrap (noWrap)                                                */
/*---------------------------------------------------------------------*/
/**  Determine whether the text inside the legend is wrapped
  *
  *  @param {Boolean} noWrap   True if the text in the legend needs to wrap
  *
  */
dvt.Palette.prototype.setNoWrap = function(noWrap)
{
  this._noWrap = noWrap;
};


/*---------------------------------------------------------------------*/
/*   isNoWrap ()                                                       */
/*---------------------------------------------------------------------*/
/**
  *  @return {Boolean}  Return true if the text in the legend needs to wrap
  */
dvt.Palette.prototype.isNoWrap = function()
{
  if (this._noWrap == 'true')
    return this._noWrap;
  else
    return 'false';
};


/**
 * Updates palette sections
 * @param {string} activeSectionId id of updated section
 */
dvt.Palette.prototype.update = function(activeSectionId) {
  this._accordion.update(activeSectionId);
  var isActive = this._accordion.getSectionById(activeSectionId).isActive();

  //update activeSections
  var index = -1;
  if (this._activeSections) {
    index = dvt.ArrayUtils.getIndex(this._activeSections, activeSectionId);
  }
  if (!isActive && index > -1) {
    this._activeSections.splice(index, 1);
  }
  else if (isActive && index < 0) {
    this._activeSections.push(activeSectionId);
  }

  // Convert the activity state to a string and store on the peer
  var event = dvt.EventFactory.newAdfPropertyChangeEvent(dvt.Palette.PALETTE_ACTIVE_SECTIONS_KEY, this._activeSections.join(dvt.AdfDiagram.ROWKEY_DELIMITER));
  this._diagram.dispatchEvent(event);
};


/**
 * Parses and retrieves numeric value from max width value passed from the server
 * @param {string} rawMaxWidthValue max width value passed from the server
 * @return {number} max width value
 * @private
 */
dvt.Palette.prototype._getMaxWidthValue = function(rawMaxWidthValue) {
  if (!rawMaxWidthValue)
    return null;

  var maxWidth = dvt.StringUtils.trim(rawMaxWidthValue);
  if (dvt.StringUtils.endsWith(maxWidth, '%')) {
    maxWidth = dvt.StringUtils.trim(maxWidth.replace(/\%$/, ''));
    if (!isNaN(parseFloat(maxWidth))) {
      return parseFloat(maxWidth) / 100 * this._diagram.getWidth();
    }
    return null;
  }
  else if (dvt.StringUtils.endsWith(maxWidth, 'px')) {
    return this._getMaxWidthValue(maxWidth.replace(/px$/, ''));
  }

  return isNaN(parseFloat(maxWidth)) ? null : parseFloat(maxWidth);
};

/**
 * Creates keyboard handler for the palette
 * @param {DvtPaletteEventManager} manager Event manager.
 * @return {DvtPaletteKeyboardHandler}
 * @protected
 */
dvt.Palette.prototype.CreateKeyboardHandler = function(manager) {
  return new DvtPaletteKeyboardHandler(manager, this);
};


/**
 * Get section content by given section id
 * @param {string} sectionId section id
 * @return {DvtPaletteSection} section content
 */
dvt.Palette.prototype.getSectionContentById = function(sectionId) {
  var sectionContent = null;
  if (this._linkSection && this._linkSection.getId() == sectionId)
    sectionContent = this._linkSection;
  else {
    for (var i = 0; i < this._sectionContent.length; i++) {
      var section = this._sectionContent[i];
      if (section.getId() == sectionId) {
        sectionContent = section;
        break;
      }
    }
  }
  return sectionContent;
};

/**
 * Returns max control height when all sections are expanded
 * @return {number}
 */
dvt.Palette.prototype.getExpandedHeight = function() {
  return this._accordion.getExpandedHeight();
};

/**
 * Get accordion sections
 * @return {array} array of accordion sections
 */
dvt.Palette.prototype.getSections = function() {
  return this._accordion.getSections();
};

/**
 * @override
 */
dvt.Palette.prototype.getDimensions = function(targetCoordinateSpace) {
  var bounds = new dvt.Rectangle(0, 0, this._scrollingContainer.getWidth(), this._scrollingContainer.getHeight());
  if (!targetCoordinateSpace || targetCoordinateSpace === this)
    return bounds;
  else { // Calculate the bounds relative to the target space
    return this.ConvertCoordSpaceRect(bounds, targetCoordinateSpace);
  }
};

/**
 * @override
 */
dvt.Palette.prototype.getDimensionsWithStroke = function(targetCoordinateSpace) {
  return this.getDimensions(targetCoordinateSpace);
};

/**
 * Enables/disables user interaction
 * @param {boolean} bEnabled True to enable interaction
 */
dvt.Palette.prototype.setInteractionEnabled = function(bEnabled) {
  if (!bEnabled)
    this._eventManager.removeListeners(this);
  else
    this._eventManager.addListeners(this);
};

/**
 * Returns true if palette has link section
 * @return {boolean} true if palette has link section
 */
dvt.Palette.prototype.hasLinkSection = function() {
  return this._linkSection ? true : false;
};
// Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
/**
 * @constructor
 * @param {dvt.Context} context rendering context
 * @param {function} callback A function that responds to component events
 * @param {object} callbackObj The optional object instance that the callback function is defined on
 * @param {dvt.Palette} palette The palette this event manager controls
 * @extends {dvt.EventManager}
 * @class DvtPaletteEventManager
 */
var DvtPaletteEventManager = function(context, callback, callbackObj, palette) {
  this.Init(context, callback, callbackObj);
  this.setSelectionHandler(new DvtPaletteSelectionHandler());
  this._palette = palette;
};

dvt.Obj.createSubclass(DvtPaletteEventManager, dvt.EventManager, 'DvtPaletteEventManager');


/**
 * @override
 */
DvtPaletteEventManager.prototype.OnClick = function(event) {
  event.stopPropagation();
  var obj = this.GetLogicalObject(this.GetCurrentTargetForEvent(event));
  DvtPaletteEventManager.superclass.OnClick.call(this, event);

  if (obj instanceof dvt.AccordionSection) {
    this._palette.update(obj.getId());
  }
};


/**
 * @override
 */
DvtPaletteEventManager.prototype.HandleTouchClickInternal = function(event) {
  var obj = this.GetLogicalObject(this.GetCurrentTargetForEvent(event));

  if (obj instanceof dvt.AccordionSection) {
    this._palette.update(obj.getId());
  }
};
// Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
/*---------------------------------------------------------------------------------*/
/*  DvtPaletteKeyboardHandler     Keyboard handler for palette                     */
/*---------------------------------------------------------------------------------*/
/**
 * Keyboard handler for the palette component
 * @param {dvt.EventManager} manager The owning dvt.EventManager
 * @param {dvt.Palette} palette The owning palette component
 * @class DvtPaletteKeyboardHandler
 * @constructor
 * @extends {dvt.KeyboardHandler}
 */
var DvtPaletteKeyboardHandler = function(manager, palette)
{
  this.Init(manager, palette);
};

dvt.Obj.createSubclass(DvtPaletteKeyboardHandler, dvt.KeyboardHandler, 'DvtPaletteKeyboardHandler');


/**
 * @override
 */
DvtPaletteKeyboardHandler.prototype.Init = function(manager, palette) {
  DvtPaletteKeyboardHandler.superclass.Init.call(this, manager);
  this._palette = palette;
};

/**
 * @override
 */
DvtPaletteKeyboardHandler.prototype.processKeyDown = function(event) {
  var keyCode = event.keyCode;
  var currentNavigable = this._eventManager.getFocus();
  if (keyCode == dvt.KeyboardEvent.TAB) {
    var next = null;
    dvt.EventManager.consumeEvent(event);
    if (!currentNavigable) {
      var sections = this._palette.getSections();
      if (sections.length > 0) {
        next = sections[0];
      }
    }
    else {
      next = currentNavigable;
    }
    return next;
  }
  else if (keyCode == dvt.KeyboardEvent.ENTER ||
      keyCode == dvt.KeyboardEvent.LEFT_ARROW ||
      keyCode == dvt.KeyboardEvent.RIGHT_ARROW ||
      keyCode == dvt.KeyboardEvent.SPACE) {

    if (currentNavigable && currentNavigable instanceof dvt.AccordionSection) {
      //expand/collapse section if necessary
      if ((currentNavigable.isActive() && keyCode == dvt.KeyboardEvent.LEFT_ARROW) ||
          (!currentNavigable.isActive() && keyCode == dvt.KeyboardEvent.RIGHT_ARROW) ||
          keyCode == dvt.KeyboardEvent.ENTER ||
          keyCode == dvt.KeyboardEvent.SPACE) {
        this._palette.update(currentNavigable.getId());
      }
    }
    else if (currentNavigable && currentNavigable instanceof DvtPaletteItem) {
      currentNavigable.HandleKeyboardEvent(event);
    }
    return null;
  }
  else if (keyCode == dvt.KeyboardEvent.N && event.ctrlKey && event.altKey) {
    if (currentNavigable && currentNavigable instanceof DvtPaletteItem) {
      var editor = this._palette.getDiagram().getEditor();
      if (editor && this._palette.getDiagram().isStickyNodeEnabled()) {
        editor.processNodeCreateWithKeyboard(null);
      }
    }
    return null;
  }
  else {
    return DvtPaletteKeyboardHandler.superclass.processKeyDown.call(this, event);
  }
};


/**
 * Get next navigable object in the palette
 * @param {dvt.AccordionSection|DvtPaletteItem} currentNavigable a current navigable
 * @param {dvt.KeyboardEvent} event a keyboard event
 * @return {dvt.AccordionSection|DvtPaletteItem} next navigable
 */
DvtPaletteKeyboardHandler.prototype.getNextNavigable = function(currentNavigable, event) {
  if (!(event.keyCode == dvt.KeyboardEvent.DOWN_ARROW || event.keyCode == dvt.KeyboardEvent.UP_ARROW))
    return currentNavigable;

  var next = null;
  var bForward = (event.keyCode == dvt.KeyboardEvent.DOWN_ARROW) ? true : false;
  var itemIdx = -1;
  var navigables = this._getNavigables();
  for (var i = 0; i < navigables.length; i++) {
    if (currentNavigable == navigables[i]) {
      if (bForward)
        itemIdx = (i == navigables.length - 1) ? -1 : i + 1;
      else
        itemIdx = (i == 0) ? - 1 : i - 1;
      break;
    }
  }
  if (itemIdx >= 0)
    next = navigables[itemIdx];
  return next;
};


/**
 * Get palette navigables section and visible palette items
 * @return {array} an array of palette navigables
 * @private
 */
DvtPaletteKeyboardHandler.prototype._getNavigables = function() {
  var navigables = [];
  var sections = this._palette.getSections();
  for (var i = 0; i < sections.length; i++) {
    var section = sections[i];
    navigables.push(section);
    if (section.isActive()) {
      var sectionContent = this._palette.getSectionContentById(section.getId());
      var sectionItems = sectionContent.getItems() || [];
      for (var j = 0; j < sectionItems.length; j++) {
        navigables.push(sectionItems[j]);
      }
    }
  }
  return navigables;
};
// Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
/**
 * @constructor
 * Palette item
 * @param {dvt.Context} context The rendering context
 * @param {dvt.Palette} palette Parent palette
 * @param {string} paletteId Type hint for the item
 * @param {string} stampDefinition Object id
 * @param {string} label Label text for the item
 * @param {string} dragFlavor optional drag flavor for the palette item
 * @param {Object} styleMap Style map for the item
 * @class DvtPaletteItem
 */
var DvtPaletteItem = function(context, palette, paletteId, stampDefinition, label, dragFlavor, styleMap)
{
  this.Init(context, palette, paletteId, stampDefinition, label, dragFlavor, styleMap);
};

dvt.Obj.createSubclass(DvtPaletteItem, dvt.Obj, 'DvtPaletteItem');


/**
 * @protected
 * Helper method called by constructor
 * @param {dvt.Context} context Context object
 * @param {dvt.Palette} palette Parent palette
 * @param {string} paletteId Type hint for the item
 * @param {string} id Object id
 * @param {string} labelText Label text for the item
 * @param {string} dragFlavor optional drag flavor for the palette item
 * @param {Object} styleMap Style map for the item
 */
DvtPaletteItem.prototype.Init = function(context, palette, paletteId, id, labelText, dragFlavor, styleMap) {
  this._context = context;
  this._paletteId = paletteId;
  this._id = id;
  this._palette = palette;
  this._paletteStamp = null;
  this._labelText = labelText;
  this._label = null;
  this._focusRect = null;
  this._itemContainer = new dvt.Container(this._context);
  this.DragFlavor = dragFlavor && this._palette.getDiagram().ConvertDataFlavorFunc ?
      this._palette.getDiagram().ConvertDataFlavorFunc('javax.faces.component.UIComponent/' + dragFlavor) : null;

  //styles
  this._styleMap = styleMap;
};


/**
 * Positions palette item label and shape.
 * The function is called by DvtPaletteSection.render() without width on initial rendering.
 * It will be called second time for bidi to adjust label and image layout when item width is known.
 * @param {number} width available width, the value will be null on the initial rendering
 */
DvtPaletteItem.prototype.layout = function(width) {
  var shape = this.getPaletteVisualComponent().getDisplayObj();
  shape.setMouseEnabled(false);
  var itemPaddingX = this._styleMap['sectionItem']['itemPaddingX'];
  var itemPaddingY = this._styleMap['sectionItem']['itemPaddingY'];
  var labelPaddingX = this._styleMap['sectionItem']['labelPaddingX'];
  var labelDim = this.getLabelDimensions();
  var shapeDim = this.getShapeDimensions();
  var itemHeight = Math.max(this._styleMap['sectionItem']['minHeight'], shapeDim ? shapeDim.h : 0);

  if (dvt.Agent.isRightToLeft(this._context) && width) {

    if (shape && shapeDim) {
      shape.setTranslate(width - shapeDim.w - itemPaddingX, itemPaddingY);
    }

    if (this._label && labelDim) {
      this._label.setTranslate(width -
          (this.GetLabelOffsetX() ? itemPaddingX + this.GetLabelOffsetX() + labelPaddingX + labelDim.w :
          itemPaddingX + labelDim.w),
          (itemHeight - labelDim.h) / 2 + itemPaddingY);
    }
  }
  else {
    if (shape) {
      shape.setTranslate(itemPaddingX, itemPaddingY);
    }

    if (this._label && labelDim) {
      this._label.setTranslate(this.GetLabelOffsetX() ?
          itemPaddingX + this.GetLabelOffsetX() + labelPaddingX :
          itemPaddingX,
          (itemHeight - labelDim.h) / 2 + itemPaddingY);
    }
  }

  if (!this._focusRect) {
    var focusWidth = this.GetLabelOffsetX() + itemPaddingX + (labelDim ? labelDim.w + 2 * labelPaddingX : itemPaddingX);
    var focusHeight = itemHeight + 2 * itemPaddingY;
    this._addFocusRect(focusWidth, focusHeight);
  }
  this.addAccessibilityAttributes();
};


/**
 * Renders label and adds it to the container
 * @param {number} width Max width for the label
 * @param {number} height Label height
 * @protected
 */
DvtPaletteItem.prototype.RenderLabel = function(width, height) {
  if (this._labelText) {
    this._label = new dvt.OutputText(this._context, this._labelText);
    this._label.setCSSStyle(this._styleMap['sectionItem']['labelStyle']);
    dvt.TextUtils.fitText(this._label, width - 2 * this._styleMap['sectionItem']['labelPaddingX'], height, this._itemContainer);
    var labelDims = this._label.measureDimensions();
    this.setLabelDimensions(labelDims);
  }
};


/**
 * Adds focus rectangle to the palette item
 * @param {number} width focus rectangle width
 * @param {number} height focus rectangle height
 * @private
 */
DvtPaletteItem.prototype._addFocusRect = function(width, height) {
  this._focusRect = new dvt.Rect(this._context, 0, 0, width, height);
  this._focusRect.setInvisibleFill();
  this._focusRect.setRx(this._styleMap['sectionItem']['borderRadius']);
  this._itemContainer.addChildAt(this._focusRect, 0);
};


/**
 * Renders shape and adds it to the container
 * @protected
 * @param {number} ww width
 * @param {number} hh height
 */
DvtPaletteItem.prototype.RenderShape = function(ww, hh) {
  var afItemContainer = this.getPaletteVisualComponent();
  if (afItemContainer) {
    var afContext = new dvt.AfContext(this._context, this._palette.getEventManager());
    afContext.setSkinName(this._palette.getDiagram().getSkinName());
    afContext.setAvailableWidth(ww);
    afContext.setAvailableHeight(hh);

    dvt.AfComponentFactory.doLayout(afContext, afItemContainer, this._itemContainer);
    this.setShapeDimensions(afItemContainer.getBounds());
  }
};


/**
 * Sets label offset. It is set by a palette section after images/shapes are rendered and the  largest image size is known.
 * It is used to align labels in the section.
 * @param {number} offsetX label offset
 * @protected
 */
DvtPaletteItem.prototype.SetLabelOffsetX = function(offsetX) {
  this._labelOffsetX = offsetX;
};


/**
 * Gets label offset. Used by item layout.
 * @return {number} label offset
 */
DvtPaletteItem.prototype.GetLabelOffsetX = function() {
  return this._labelOffsetX;
};


/**
 * Gets dimensions of the palette item
 * @return {dvt.Rectangle} the bounds of the displayable relative to the target coordinate space.
 */
DvtPaletteItem.prototype.getDimensions = function()
{
  return dvt.DisplayableUtils.getDimForced(this._context, this._itemContainer);
};


/**
 * Gets dimensions of the palette item shape
 * @return {dvt.Rectangle} the bounds of the displayable relative to the target coordinate space.
 */
DvtPaletteItem.prototype.getShapeDimensions = function()
{
  return this._shapeDims;
};


/**
 * Sets dimensions of the palette item shape
 * @param {dvt.Rectangle} dim the bounds of the displayable relative to the target coordinate space
 */
DvtPaletteItem.prototype.setShapeDimensions = function(dim)
{
  this._shapeDims = dim;
};


/**
 * Sets dimensions of the palette item label
 * @param {dvt.Rectangle} dim the bounds of the displayable relative to the target coordinate space
 */
DvtPaletteItem.prototype.setLabelDimensions = function(dim) {
  this._labelDims = dim;
};


/**
 * Gets dimensions of the palette item label
 * @return {dvt.Rectangle} the bounds of the displayable relative to the target coordinate space.
 */
DvtPaletteItem.prototype.getLabelDimensions = function() {
  return this._labelDims;
};


/**
 * Gets displayable container for the shape, label and the focus rectangle
 * @return {dvt.Container} displayable container for the palette item
 */
DvtPaletteItem.prototype.getDisplayableContainer = function() {
  return this._itemContainer;
};


/**
 * Get top af component for the palette item
 * subclasses should override
 * return {dvt.AfComponent} top af component for the palette item
 */
DvtPaletteItem.prototype.getPaletteVisualComponent = function() {
  // subclasses should override
};


/**
 * Get item template
 * @return {dvt.AfComponent|dvt.AdfDiagramLinkDef} item template
 */
DvtPaletteItem.prototype.getTemplateDef = function() {
  var stamp = null;
  var id = this._id;
  stamp = this._palette._stampDefs[id];
  return stamp;
};


/**
 * Check whether the item can be selected
 * @return {boolean} true if the items is selectable
 */
DvtPaletteItem.prototype.isSelectable = function() {
  return true;
};


/**
 * Check whether the item is selected
 * @return {boolean} true if the item is selected
 */
DvtPaletteItem.prototype.isSelected = function() {
  return this._selected;
};


/**
 * Adds or removes selection effect to the item
 * @param {boolean} selected True if the item is selected
 */
DvtPaletteItem.prototype.setSelected = function(selected) {
  this._selected = selected;
  if (selected) {
    this._focusRect.setFill(new dvt.SolidFill(this._styleMap['sectionItem']['fillColorSelected']));
  }
  else {
    this._focusRect.setInvisibleFill();
  }
  this.updateAccessibilityAttributes();
};


/**
 * Adjusts focus dimensions and item layout after all sections are rendered
 * @param {number} itemWidth item width
 * @protected
 */
DvtPaletteItem.prototype.AdjustItemDimensions = function(itemWidth) {
  if (dvt.Agent.isRightToLeft(this._context)) {
    //adjust item layout after we know the final width for the item
    this.layout(itemWidth);
    this._focusRect.setWidth(itemWidth);
  }
  else {
    this._focusRect.setWidth(itemWidth);
  }
};


/**
 * Shows the hover effect.
 */
DvtPaletteItem.prototype.showHoverEffect = function() {
  if (!this._focusRect)
    return;

  if (!this._selected) {
    this._focusRect.setFill(new dvt.SolidFill(this._styleMap['sectionItem']['fillColorHover']));
  }
};


/**
 * Hides the hover effect.
 */
DvtPaletteItem.prototype.hideHoverEffect = function() {
  if (!this._selected) {
    this._focusRect.setInvisibleFill();
  }
};


//---------------------------------------------------------------------//
// Keyboard Support: DvtKeyboardNavigable impl                        //
//---------------------------------------------------------------------//
/**
 * @override
 */
DvtPaletteItem.prototype.getKeyboardBoundingBox = function(targetCoordinateSpace) 
{
  var bounds = this._focusRect.getDimensions();
  var stageP1 = this.getDisplayableContainer().localToStage(new dvt.Point(bounds.x, bounds.y));
  var stageP2 = this.getDisplayableContainer().localToStage(new dvt.Point(bounds.x + bounds.w, bounds.y + bounds.h));
  return new dvt.Rectangle(stageP1.x, stageP1.y, stageP2.x - stageP1.x, stageP2.y - stageP1.y);
};


/**
 * @override
 */
DvtPaletteItem.prototype.getTargetElem = function() 
{
  return this.getDisplayableContainer().getElem();
};

/**
 * @override
 */
DvtPaletteItem.prototype.showKeyboardFocusEffect = function() {
  this._isShowingKeyboardFocusEffect = true;
  this.showHoverEffect();
  this._context.setActiveElement(this.getDisplayableContainer()); //accessibility feature
};


/**
 * @override
 */
DvtPaletteItem.prototype.hideKeyboardFocusEffect = function() {
  this._isShowingKeyboardFocusEffect = false;
  this.hideHoverEffect();
};


/**
 * @override
 */
DvtPaletteItem.prototype.isShowingKeyboardFocusEffect = function() {
  return this._isShowingKeyboardFocusEffect;
};


/**
 * @override
 */
DvtPaletteItem.prototype.getNextNavigable = function(event) 
{
  var keyboardHandler = this._palette.getEventManager().getKeyboardHandler();
  return keyboardHandler.getNextNavigable(this, event);
};

/**
 * Handle "Enter" event
 * @param {dvt.KeyboardEvent} event a keyboard event
 * @protected
 */
DvtPaletteItem.prototype.HandleKeyboardEvent = function(event) {
  var keyCode = event.keyCode;
  if (keyCode == dvt.KeyboardEvent.ENTER || keyCode == dvt.KeyboardEvent.SPACE) {
    var eventManager = this._palette.getEventManager();
    var target = this.getDisplayableContainer();
    var point = target.localToStage(new dvt.Point(0, 0));
    var mouseEvent = dvt.DomEventFactory.generateMouseEventFromKeyboardEvent(event, this._context, dvt.MouseEvent.CLICK,
        this._context.getStage(),
        point.x, point.y);
    mouseEvent.target = target;
    eventManager.PreOnClick(mouseEvent);
  }
};

//---------------------------------------------------------------------//
// Accessibility Support                                               //
//---------------------------------------------------------------------//
/**
 * Adds WAI-ARIA attributes to the object
 */
DvtPaletteItem.prototype.addAccessibilityAttributes = function() {
  // WAI-ARIA
  var desc = this.getAriaLabel();
  this.getDisplayableContainer().setAriaRole('group');
  if (!dvt.Agent.deferAriaCreation()) {
    this.getDisplayableContainer().setAriaProperty('label', desc);
  }
};


/**
 * Updates WAI-ARIA attributes for the link
 */
DvtPaletteItem.prototype.updateAccessibilityAttributes = function() {
  if (!dvt.Agent.deferAriaCreation()) {
    var desc = this.getAriaLabel();
    if (desc) {
      this.getDisplayableContainer().setAriaProperty('label', desc);
    }
  }
};


/**
 * Gets WAI-ARIA label attribute
 * @return {string} WAI-ARIA label
 */
DvtPaletteItem.prototype.getAriaLabel = function() {
  var states = [];
  states.push(this._selected ? dvt.Bundle.getTranslatedString(dvt.Bundle.UTIL_PREFIX, 'STATE_SELECTED') : dvt.Bundle.getTranslatedString(dvt.Bundle.UTIL_PREFIX, 'STATE_UNSELECTED'));
  return dvt.Displayable.generateAriaLabel(this._labelText, states);
};



/**
 * @constructor
 * @extends {DvtPaletteItem}
 * Sticky palette item
 * @param {dvt.Context} context The rendering context
 * @param {dvt.Palette} palette Parent palette
 * @param {string} paletteId Type hint for the item
 * @param {string} stampDefinition Object id
 * @param {string} label Label text for the item
 * @param {string} dragFlavor optional drag flavor for the palette item
 * @param {object} styleMap Style map for the item
 */
var DvtPaletteStickyItem = function(context, palette, paletteId, stampDefinition, label, dragFlavor, styleMap)
{
  this.Init(context, palette, paletteId, stampDefinition, label, dragFlavor, styleMap);
};

dvt.Obj.createSubclass(DvtPaletteStickyItem, DvtPaletteItem, 'DvtPaletteStickyItem');


/**
 * Gets an object that represents sticky item id. Used by selection handler, palette event handler in diagram
 * @return {object} object that represents item id
 */
DvtPaletteStickyItem.prototype.getId = function() {
  var ret = new Object();
  ret.paletteId = this._paletteId;
  ret.def = this.getTemplateDef();
  ret.type = DvtPaletteLinkItem.NODE_TYPE;
  ret.id = this._id;
  return ret;
};

/**
 * @override
 */
DvtPaletteStickyItem.prototype.getPaletteVisualComponent = function() {
  if (this._paletteStamp) {
    return this._paletteStamp;
  }
  var drawableComp = this.getTemplateDef();
  var stamp = new dvt.AfPanelGroupLayout();
  stamp.setLayout(dvt.AfPanelGroupLayout.LAYOUT_VERTICAL);
  stamp.addChild(drawableComp);
  this._paletteStamp = stamp;
  return this._paletteStamp;
};

/**
 * @override
 */
DvtPaletteStickyItem.prototype.isDragAvailable = function(clientIds) {
  return clientIds[0];
};

/**
 * Returns an object containing the drag context info
 * @param {number} mouseX
 * @param {number} mouseY
 * @return {object} a map containing the id, template of the palette item, item flavor and object type (to be wrapped by the peer)
 */
DvtPaletteStickyItem.prototype.getDragContext = function(mouseX, mouseY) {
  return {'id': this._paletteId, 'template': this.getTemplateDef(), 'dragFlavor': this.DragFlavor, 'typeName': this.getTypeName()};
};

/**
 * Returns the transferable object for a drag
 * @param {number} mouseX
 * @param {number} mouseY
 * @return {array} transferable object for a drag
 */
DvtPaletteStickyItem.prototype.getDragTransferable = function(mouseX, mouseY) {
  var trans = new Array();
  return trans;
};

/**
 * Returns the feedback for the drag operation
 * @param {number} mouseX
 * @param {number} mouseY
 * @return {array} an array that contains a displayable for the drag operation
 */
DvtPaletteStickyItem.prototype.getDragFeedback = function(mouseX, mouseY) {
  var displayables = [];
  this.setSelected(false);
  displayables.push(this.getPaletteVisualComponent().getContentShape());
  return displayables;
};


/**
 * @constructor
 * @extends {DvtPaletteItem}
 * Link item
 * @param {dvt.Context} context Context object
 * @param {dvt.Palette} palette Parent palette
 * @param {string} paletteId Type hint for the item
 * @param {string} stampDefinition Object id
 * @param {string} label Label text for the item
 * @param {string} dragFlavor optional drag flavor for the palette item
 * @param {Object} styleMap Style map for the item
 */
var DvtPaletteLinkItem = function(context, palette, paletteId, stampDefinition, label, dragFlavor, styleMap)
{
  this.Init(context, palette, paletteId, stampDefinition, label, dragFlavor, styleMap);
};

dvt.Obj.createSubclass(DvtPaletteLinkItem, DvtPaletteItem, 'DvtPaletteLinkItem');

/**
 * Link type
 * @const
 */
DvtPaletteLinkItem.LINK_TYPE = 'link';
/**
 * Node type
 * @const
 */
DvtPaletteLinkItem.NODE_TYPE = 'node';


/**
 * Gets an object that represents item id. Used by selection handler, palette event handler in diagram
 * @return {object} object that represents item id
 */
DvtPaletteLinkItem.prototype.getId = function() {
  var ret = new Object();
  ret.paletteId = this._paletteId;
  ret.def = this.getTemplateDef();
  ret.type = DvtPaletteLinkItem.LINK_TYPE;
  ret.id = this._id;
  return ret;
};

/**
 * @override
 */
DvtPaletteLinkItem.prototype.getPaletteVisualComponent = function() {
  if (this._paletteStamp) {
    return this._paletteStamp;
  }
  var linkData = new DvtAdfDiagramLinkData();
  var linkDef = this.getTemplateDef();
  var link = new dvt.AdfDiagramLink(this._context, linkData, this._palette.getDiagram());
  link.setLinkDef(linkDef);
  link.setSelectable(false);
  link.setPoints([0, 0, link.getLinkLength(), 0]);
  var drawableComp = new DvtAfPaletteLink(link, this._styleMap);
  var stamp = new dvt.AfPanelGroupLayout();
  stamp.setLayout(dvt.AfPanelGroupLayout.LAYOUT_VERTICAL);
  stamp.addChild(drawableComp);
  this._paletteStamp = stamp;
  return this._paletteStamp;
};



/**
 * @constructor
 * @param {dvt.AdfDiagramLink} drawableContent
 * @param {object} styleMap palette link styles
 */
var DvtAfPaletteLink = function(drawableContent, styleMap) {
  this._drawableContent = drawableContent;
  this._styleMap = styleMap;
  this.Init();
};

dvt.Obj.createSubclass(DvtAfPaletteLink, dvt.AfComponent, 'DvtAfPaletteLink');

/**
 * Tag name
 * @const
 */
DvtAfPaletteLink.TAG_NAME = 'paletteLink';

dvt.AfComponentFactory.registerComponent(DvtAfPaletteLink.TAG_NAME, DvtAfPaletteLink);


/**
 * Get default styles
 * @return {object} style map
 * @protected
 */
DvtAfPaletteLink.prototype.GetDefaultStyles = function() {
  return DvtAfPaletteLink._afStyles;
};

/**
 * Set default styles
 * @param {object} styleList style map
 * @protected
 */
DvtAfPaletteLink.prototype.SetDefaultStyles = function(styleList) {
  DvtAfPaletteLink._afStyles = styleList;
};


/**
 * @override
 */
DvtAfPaletteLink.prototype.createContentShape = function(dvtContext) {
  // create a container for separator
  var id = this.getId();
  if (!id) {
    id = '_PaletteLinkItem' + dvt.AfComponent.genUniqueId();
  }
  var group = new dvt.Container(dvtContext, id);
  this._dvtContext = dvtContext;
  group.addChild(this._drawableContent);
  return group;
};


/**
 * @override
 */
DvtAfPaletteLink.prototype.renderSelfBeforeChildren = function() {
  this._drawableContent.renderLink();
  var dim = this._drawableContent.getDimensions();
  var bgRect = new dvt.Rect(this._dvtContext, 0, 0, dim.w + 2 * this._styleMap['linkItem']['paddingX'], dim.h + 2 * this._styleMap['linkItem']['paddingY']);
  bgRect.setInvisibleFill();
  this._drawableContent.setTranslate(- dim.x + this._styleMap['linkItem']['paddingX'],
      - dim.y + this._styleMap['linkItem']['paddingY']);
  this._drawableContent.getParent().addChildAt(bgRect, 0);
  this._drawableContent._shape._logicalObject = null;
};


/**
 * Get palette link bounds
 * @return {dvt.Rectangle} palette link bounds
 */
DvtAfPaletteLink.prototype.getBounds = function() {
  var dim = this._drawableContent.getParent().getDimensions();
  return new dvt.Rectangle(0, 0, dim.w, dim.h);
};


/**
 * @override
 */
DvtAfPaletteLink.prototype.getTagName = function() {
  return DvtAfPaletteLink.TAG_NAME;
};


/**
 * Check whether the item has background shape
 * @return {boolean} item of this type always returns false
 * @private
 */
DvtAfPaletteLink.prototype._hasBgShape = function() {
  return false;//dvt.AfComponent._CREATE_BG_SHAPE;
};
// Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
/**
 * DvtPaletteSection
 * @constructor
 * @param {dvt.Palette} palette Palette that contains this section (parent)
 * @param {string} id Section id
 * @param {string} label Section label
 * @param {Object} styleMap for the control
 */
var DvtPaletteSection = function(mainComp, id, label, styleMap)
{
  this.Init(mainComp, id, label, styleMap);

};

dvt.Obj.createSubclass(DvtPaletteSection, dvt.Obj, 'DvtPaletteSection');


/**
 * @protected
 * Initialization method called by the constructor
 * @param {dvt.Palette} palette Palette that contains this section (parent)
 * @param {string} id Section id
 * @param {string} label Section label
 * @param {Object} styleMap for the control
 */
DvtPaletteSection.prototype.Init = function(palette, id, label, styleMap) {
  this._id = id;
  this._label = label;
  this._items = new Array();
  this._palette = palette;
  this._bDisclosed = false;
  this._context = palette.getCtx();
  this._styleMap = styleMap;
};


DvtPaletteSection.prototype.getId = function() {
  return this._id;
};


DvtPaletteSection.prototype.getLabel = function() {
  return this._label;
};


DvtPaletteSection.prototype.addItem = function(item) {
  return this._items.push(item);
};


DvtPaletteSection.prototype.getItems = function() {
  return this._items;
};


/**
 * Updates disclosed state for the setion. Default value is false
 * @param {boolean} bDisclosed True for disclosed state
 */
DvtPaletteSection.prototype.setDisclosed = function(bDisclosed) {
  var val = false;
  if (typeof bDisclosed === 'boolean')
    val = bDisclosed;
  else if (typeof bDisclosed === 'string')
    val = (bDisclosed.toLowerCase() === 'true');
  this._bDisclosed = val;
};


/**
 * Gets disclosed state for the section
 * @return {boolean} True if the section is disclosed
 */
DvtPaletteSection.prototype.isDisclosed = function() {
  return this._bDisclosed;
};


/**
 * Sets an id of the selected item in the section
 * @param {string} selectedItemId Selected item id
 */
DvtPaletteSection.prototype.setSelectedItemId = function(selectedItemId) {
  this._selectedItemId = selectedItemId;
};


/**
 * Gets an id of the selected item in the section
 * @return {string} Selected item id
 */
DvtPaletteSection.prototype.getSelectedItemId = function() {
  return this._selectedItemId;
};


/**
 * Returns a selected item for the section
 * @return {DvtPaletteSection} Selected palette section
 */
DvtPaletteSection.prototype.getSelectedItem = function() {

  var items = this.getItems();
  var selectedId = this.getSelectedItemId();
  if (!selectedId && items.length > 0)
    return items[0];

  for (var i = 0; i < items.length; i++) {
    if (items[i].getId().id === selectedId)
      return items[i];
  }
  return null;
};


/**
 * Renders palette section
 * @param {dvt.Container} parent Parent container for the section
 * @param {number} ww Section width
 * @param {number} hh Section height
 */
DvtPaletteSection.prototype.render = function(parent, ww, hh) {

  var maxShapeWidth = 0;
  var spacerHeight = 2 * this._styleMap['sectionItem']['itemPaddingY'];
  var offsetY = 0;
  if (this._items.length > 0) {
    //create shape and find labelOffset for the section
    for (var i = 0; i < this._items.length; i++) {
      var item = this._items[i];
      parent.addChild(item.getDisplayableContainer());
      item.RenderShape(ww, hh);
      var shapeDim = item.getShapeDimensions();
      if (shapeDim && maxShapeWidth < shapeDim.w)
        maxShapeWidth = shapeDim.w;
    }
    //position shape, create and position label
    for (var i = 0; i < this._items.length; i++) {
      var item = this._items[i];
      var itemHeight = Math.max(this._styleMap['sectionItem']['minHeight'], item.getShapeDimensions() ? item.getShapeDimensions().h : 0);
      item.SetLabelOffsetX(maxShapeWidth);
      item.RenderLabel(ww - maxShapeWidth, itemHeight);
      item.layout();
      item.getDisplayableContainer().setTranslate(0, offsetY);
      offsetY += (itemHeight + spacerHeight);
    }
  }
};
// Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
/**
  * @constructor
  * Interactivity manager for palette selection.
  */
var DvtPaletteSelectionHandler = function() {
  this.Init();
};

dvt.Obj.createSubclass(DvtPaletteSelectionHandler, dvt.SelectionHandler, 'DvtPaletteSelectionHandler');

DvtPaletteSelectionHandler.prototype.Init = function() {
  this._type = dvt.SelectionHandler.TYPE_SINGLE;
  DvtPaletteSelectionHandler.superclass.Init.call(this, this._type);
};

DvtPaletteSelectionHandler.prototype.processClick = function(target, isMultiSelect) {
  var bChanged = false;// Keep track of whether the selection is changed
  if (target && target.isSelectable && target.isSelectable()) {
    if (target.isSelected()) {
      if (target instanceof DvtPaletteStickyItem)
        bChanged = this._clearSelectionExceptLink();
    }
    else {
      bChanged = this._addToSelectionExceptLink(target, false);
    }
  }
  else {
    bChanged = this._clearSelectionExceptLink();
  }
  // Return whether the selection has changed.
  return bChanged;
};

DvtPaletteSelectionHandler.prototype._addToSelectionExceptLink = function(target, bAddToExisting) {
  // If already selected, return. This intentionally ignores bAddToExisting.
  if (target.isSelected()) {
    return false;
  }
  // If not adding to the current selection, deselect all
  if (!bAddToExisting) {
    if (target instanceof DvtPaletteStickyItem) {
      this._clearSelectionExceptLink();
    }
    else if (target instanceof DvtPaletteLinkItem) {
      this._clearSelectionExceptNode();
    }
  }
  // Finally, select the object
  target.setSelected(true);
  this._selection.push(target);
  return true;
};


/**
 * Clears the current selection.
 * @return {boolean} true if the selection has changed.
 */
DvtPaletteSelectionHandler.prototype._clearSelectionExceptLink = function() {
  if (this._selection.length <= 0)
    return false;
  var retain = null;
  for (var i = 0; i < this._selection.length; i++) {
    var selected = this._selection[i];
    if (selected instanceof DvtPaletteLinkItem) {
      retain = selected;
    }
    else {
      selected.setSelected(false);
    }
  }
  this._selection = new Array();
  if (retain)
    this._selection.push(retain);
  return true;
};

DvtPaletteSelectionHandler.prototype._clearSelectionExceptNode = function() {
  if (this._selection.length <= 0)
    return false;
  var retain = null;
  for (var i = 0; i < this._selection.length; i++) {
    var selected = this._selection[i];
    if (selected instanceof DvtPaletteStickyItem) {
      retain = selected;
    }
    else {
      selected.setSelected(false);
    }
  }
  this._selection = new Array();
  if (retain)
    this._selection.push(retain);
  return true;
};
// Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
/**
 * Default values and utility functions for component versioning.
 * @class
 * @constructor
 * @extends {dvt.BaseComponentDefaults}
 */
var DvtPaletteDefaults = function() {
  this.Init({'skyros': DvtPaletteDefaults.VERSION_1, 'alta': DvtPaletteDefaults.SKIN_ALTA});
};

dvt.Obj.createSubclass(DvtPaletteDefaults, dvt.BaseComponentDefaults, 'DvtPaletteDefaults');


/**
 * Defaults for version 1.
 */
DvtPaletteDefaults.VERSION_1 = {
  'skin': dvt.CSSStyle.SKIN_ALTA,
  'paddingX': 0,
  'paddingY': 5,
  'minWidth': 150,
  'maxWidth': 300,
  'sectionItem': {
    'labelStyle': new dvt.CSSStyle(dvt.BaseComponentDefaults.FONT_FAMILY_ALTA_12 + 'color:#333333;'),
    'labelPaddingX': 10,
    'fillColorSelected': '#CFE8FC',
    'fillColorHover': '#ebeced',
    'borderRadius': 2,
    'minHeight': 24,
    'itemPaddingX': 5,
    'itemPaddingY': 5
  },
  'linkItem': {
    'paddingX': 10,
    'paddingY': 10
  }
};

DvtPaletteDefaults.SKIN_ALTA = {
};
/**
 * @constructor
 * @extends {dvt.Obj}
 * @class DvtScrollbarEvent
 * <p>
 */
var DvtScrollbarEvent = function(type, scrollbar, startCoord, endCoord,
                                valueDelta, scaledDelta) {
  this.Init(type, scrollbar, startCoord, endCoord, valueDelta, scaledDelta);
};


dvt.Obj.createSubclass(DvtScrollbarEvent, dvt.BaseComponentEvent, 'DvtScrollbarEvent');


DvtScrollbarEvent.SB_MOVE = 'scrollbarMove';
DvtScrollbarEvent.SB_MOVE_COMPLETE = 'scrollbarMoveComplete';
DvtScrollbarEvent.SB_RESIZE = 'scrollbarResize';
DvtScrollbarEvent.SB_RESIZE_COMPLETE = 'scrollbarResizeComplete';


/**
 * Initializer
 * @protected
 */
DvtScrollbarEvent.prototype.Init = function(type, scrollbar, startCoord, endCoord, valueDelta, scaledDelta) {

  DvtScrollbarEvent.superclass.Init.call(this, type);

  this.type = type;           // TODO: temp workaround until this.type is supported in the base class
  this._sb = scrollbar;
  this._startCoord = startCoord;
  this._endCoord = endCoord;
  this._delta = valueDelta;
  this._scaledDelta = scaledDelta;
};


/**
 * Returns the id of the scrollbar
 */
DvtScrollbarEvent.prototype.getScrollbarId = function() {
  return this._sb.getUIId();
};


/**
 * Returns the start coordinate
 */
DvtScrollbarEvent.prototype.getStartCoord = function() {
  return this._startCoord;
};


/**
 * Returns the end coordinate
 */
DvtScrollbarEvent.prototype.getEndCoord = function() {
  return this._endCoord;
};


/**
 * Returns the signed number of pixels moved
 */
DvtScrollbarEvent.prototype.getMovedDelta = function() {
  return this._delta;
};


/**
 * Returns the signed number of pixels scaled by the size of the scrollbar
 */
DvtScrollbarEvent.prototype.getScaledDelta = function() {
  return this._scaledDelta;
};


/**
 * Returns the {@link DvtScrollbar} component
 */
DvtScrollbarEvent.prototype.getScrollbar = function() {
  return this._sb;
};




/*--------------------------------------------------------------------*/
/*   DvtScrollbar             Scrollbar Component                     */
/*--------------------------------------------------------------------*/
/**
  *  Scrollbar component.
  *  @extends {dvt.Container}
  *  @class DvtScrollbar component.
  *  @constructor
  */
var DvtScrollbar = function(context, x, y, w, h, isHoriz, skinName, styleMap, id) {
  this.Init(context, x, y, w, h, isHoriz, skinName, styleMap, id);
};

dvt.Obj.createSubclass(DvtScrollbar, dvt.Container, 'DvtScrollbar');

DvtScrollbar.SMALL_SCROLL_AMOUNT = 0.2;
DvtScrollbar.LARGE_SCROLL_AMOUNT = 1.0;

DvtScrollbar.MIN_THUMB_SIZE = 30;

DvtScrollbar.SCROLL_TIMER_DELAY = 50;
DvtScrollbar.SCROLL_TIMER_INITIAL_DELAY = 400;

DvtScrollbar.THUMB_TYPE = 'thumb';

// constants used for test automation
DvtScrollbar._TEST_ID_THIS = 0;
DvtScrollbar._TEST_ID_SCROLL_THUMB = 1;
DvtScrollbar._TEST_ID_SCROLL_BUTTON_DOWN = 2;
DvtScrollbar._TEST_ID_SCROLL_BUTTON_UP = 3;

DvtScrollbar.BACKGROUND = 'scrollbarBackground';
DvtScrollbar.BORDER_COLOR = 'scrollbarBorderColor';
DvtScrollbar.HANDLE_COLOR = 'scrollbarHandleColor';
DvtScrollbar.HANDLE_ACTIVE_COLOR = 'scrollbarHandleActiveColor';
DvtScrollbar.HANDLE_HOVER_COLOR = 'scrollbarHandleHoverColor';


/**
 *    @private
 */
DvtScrollbar.prototype.Init = function(context, x, y, w, h, isHoriz, skinName, styleMap, id) {
  DvtScrollbar.superclass.Init.call(this, context);
  this._isHoriz = isHoriz;

  this._styleMap = styleMap;
  this._skinName = skinName;

  // used to remember initial state while dragging the thumb
  this._clickDiff = new Object();
  this._clickStartPos = new Object();
  this._clickEndPos = new Object();
  this._lastMovePos = -1;

  this._container = new dvt.Rect(context, x, y, w, h, id);
  this._container.setPixelHinting(true);
  this.addChild(this._container);
  this._background = new DvtScrollbarBackground(this);
  this._container.addChild(this._background);

  // Create scroll arrows
  if (!this.IsSkinAlta()) {
    this._arrowWidth = DvtScrollbarArrow.WIDTH;
    if (this._isHoriz) {
      this._arrowDown = new DvtScrollbarArrow(this, DvtScrollbarArrow.ORIENT_LEFT);
      this._arrowUp = new DvtScrollbarArrow(this, DvtScrollbarArrow.ORIENT_RIGHT);
    } else {
      this._arrowDown = new DvtScrollbarArrow(this, DvtScrollbarArrow.ORIENT_DOWN);
      this._arrowUp = new DvtScrollbarArrow(this, DvtScrollbarArrow.ORIENT_UP);
    }
    this._container.addChild(this._arrowDown);
    this._container.addChild(this._arrowUp);
    this._container.setSolidStroke('black');
  } else {
    this._arrowWidth = 0;
  }

  // Create scroll handle
  this._thumbStartMin = this._arrowWidth;
  this._thumbStartPos = this._thumbStartMin;
  if (this._isHoriz)
    this._thumbLengthMax = w - this._arrowWidth * 2;
  else
    this._thumbLengthMax = h - this._arrowWidth * 2;
  this._thumbLength = this._thumbLengthMax;
  this._thumb = new DvtScrollbarThumb(this, this.getSkinName());
  this._container.addChild(this._thumb);

  this._scrollTimer = new dvt.Timer(context, DvtScrollbar.SCROLL_TIMER_DELAY, this.repeatScroll, this);
  this._initScrollTimer = new dvt.Timer(context, DvtScrollbar.SCROLL_TIMER_INITIAL_DELAY, this.initRepeatScroll, this);

  this._bScrolled = false;
  this._isLiveScroll = true;
};

DvtScrollbar.prototype.getStyleMap = function() {
  return this._styleMap;
};

DvtScrollbar.prototype.getSkinName = function() {
  return this._skinName;
};

DvtScrollbar.prototype.IsSkinAlta = function() {
  return (this._skinName == 'alta');
};

DvtScrollbar.prototype.isHorizontal = function() {
  return this._isHoriz;
};

DvtScrollbar.prototype.getThumbStart = function() {
  return this._thumbStartPos;
};

DvtScrollbar.prototype.getThumbLength = function() {
  return this._thumbLength;
};

DvtScrollbar.prototype.setThumbStartAndLength = function(start, length) {

  var k = 0;

  if (this._isHoriz) {
    k = (this.getWidth() - 2 * this._arrowWidth) / this.getWidth();
    this._thumbStartPos = this._arrowWidth + Math.round(start * k);
    this._thumbLength = Math.round(length * k);
  }
  else {
    k = (this.getHeight() - 2 * this._arrowWidth) / this.getHeight();
    // revert for horizontal axis
    // start = this.getHeight() - start - length;
    this._thumbStartPos = this._arrowWidth + Math.round(start * k);
    this._thumbLength = Math.round(length * k);
  }
  this._thumb.updateSize();
};

DvtScrollbar.prototype.getScrollAreaStart = function() {
  return this._thumbStartMin;
};

DvtScrollbar.prototype.getScrollAreaLength = function() {
  return this._thumbLengthMax;
};

DvtScrollbar.prototype.initRepeatScroll = function() {
  this._initScrollTimer.stop();
  this._scrollTimer.stop();
  this._scrollTimer.start();
};

DvtScrollbar.prototype.repeatScroll = function() {
  if (this._arrow && !this._arrow.isDisabled())
    this.arrowScroll();
};

DvtScrollbar.prototype.arrowScroll = function() {

  // BUG 12805200: update scrolling speed to match flash
  var delta = DvtScrollbar.SMALL_SCROLL_AMOUNT;

  this._lastMovePos = this._thumbStartPos;

  switch (this._arrow.getOrientation()) {
    case DvtScrollbarArrow.ORIENT_RIGHT:
    case DvtScrollbarArrow.ORIENT_DOWN:
      this.doScroll(delta);
      break;

    case DvtScrollbarArrow.ORIENT_LEFT:
    case DvtScrollbarArrow.ORIENT_UP:
      this.doScroll(-delta);
      break;
  }

  this._bScrolled = true;
  this._fireMoveEvent(!this._isLiveScroll);
};

DvtScrollbar.prototype.doScroll = function(delta) {
  var pos = this._thumbStartPos + Math.round(this._thumbLength * delta);

  this.moveTo(pos);
};

DvtScrollbar.prototype.moveTo = function(pos) {

  if (pos < this._thumbStartMin) {
    pos = this._thumbStartMin;
  }
  if ((pos + this._thumbLength) > (this._thumbStartMin + this._thumbLengthMax)) {
    pos = this._thumbStartMin + this._thumbLengthMax - this._thumbLength;
  }
  this._thumbStartPos = pos;
  this._thumb.updateSize();
};

DvtScrollbar.prototype.moveStart = function(pos) {

  var endPos = this._thumbStartPos + this._thumbLength;

  if (pos < this._thumbStartMin) {
    pos = this._thumbStartMin;
  }
  if (pos > (endPos - DvtScrollbar.MIN_THUMB_SIZE)) {
    pos = endPos - DvtScrollbar.MIN_THUMB_SIZE;
  }
  this._thumbStartPos = pos;
  this._thumbLength = endPos - pos;
  this._thumb.updateSize();
};

DvtScrollbar.prototype.moveEnd = function(pos) {

  if (pos < (this._thumbStartPos + DvtScrollbar.MIN_THUMB_SIZE)) {
    pos = this._thumbStartPos + DvtScrollbar.MIN_THUMB_SIZE;
  }
  if (pos > (this._thumbStartMin + this._thumbLengthMax)) {
    pos = this._thumbStartMin + this._thumbLengthMax;
  }
  this._thumbLength = pos - this._thumbStartPos;
  this._thumb.updateSize();
};

DvtScrollbar.prototype.processBackgroundClick = function(point) {
  var pos;
  var delta = 0;

  if (this._isHoriz) {
    pos = point.x - this.getX();
    if (pos < this._thumbStartPos)
      delta = -DvtScrollbar.LARGE_SCROLL_AMOUNT;
    else if (pos > this._thumbStartPos + this._thumbLength)
      delta = DvtScrollbar.LARGE_SCROLL_AMOUNT;
  }
  else {
    pos = point.y - this.getY();
    if (pos < this._thumbStartPos)
      delta = -DvtScrollbar.LARGE_SCROLL_AMOUNT;
    else if (pos > this._thumbStartPos + this._thumbLength)
      delta = DvtScrollbar.LARGE_SCROLL_AMOUNT;
  }

  if (delta != 0) {
    // remember the current pos for live scroll
    this._lastMovePos = this._thumbStartPos;
    // recalculate thumb position
    this.doScroll(delta);
    // and notify listeners
    // for live scroll, need to fire both MOVE and MOVE_COMPLETE events
    if (this._isLiveScroll)
      this._fireMoveEvent(false);
    this._fireMoveEvent(true);
  }
};

DvtScrollbar.prototype.processArrowDown = function(arrow) {
  this._arrow = arrow;

  this.arrowScroll();
  this._initScrollTimer.stop();
  this._initScrollTimer.start();
};

DvtScrollbar.prototype.handleArrowDown = function(arrow, event) {
  this.processArrowDown(arrow);
};

DvtScrollbar.prototype.processArrowUp = function(arrow) {
  this._clearTimers();
  // fire MOVE_COMPLETE, if scroll occured and live scroll
  if (this._bScrolled && this._isLiveScroll) {
    this._fireMoveEvent(true);
  }
  this._bScrolled = false;
};

DvtScrollbar.prototype._clearTimers = function() {
  this._initScrollTimer.stop();
  this._scrollTimer.stop();
};

DvtScrollbar.prototype.handleArrowUp = function(arrow, event) {
  this.processArrowUp();
};

DvtScrollbar.prototype.processThumbDragStart = function(point) {
  // note the drag start position
  if (this.isHorizontal()) {
    this._clickDiff[DvtScrollbar.THUMB_TYPE] = point.x;
  }
  else {
    this._clickDiff[DvtScrollbar.THUMB_TYPE] = point.y;
  }

  // remember the thumb start position at the time of drag start
  this._clickStartPos[DvtScrollbar.THUMB_TYPE] = this._thumbStartPos;
  // also remember if for tracking move
  this._lastMovePos = this._thumbStartPos;
};

DvtScrollbar.prototype.processThumbDragMove = function(point) {
  var pos;
  if (this.isHorizontal()) {
    pos = this._clickStartPos[DvtScrollbar.THUMB_TYPE] + (point.x - this._clickDiff[DvtScrollbar.THUMB_TYPE]);
  }
  else {
    pos = this._clickStartPos[DvtScrollbar.THUMB_TYPE] + (point.y - this._clickDiff[DvtScrollbar.THUMB_TYPE]);
  }
  this.moveTo(pos);

  this._fireMoveEvent(false);
};

DvtScrollbar.prototype.processThumbDragEnd = function(point) {
  var moveDelta = 0;

  if (this.isHorizontal())
    moveDelta = this._clickDiff[DvtScrollbar.THUMB_TYPE] - point.x;
  else
    moveDelta = this._clickDiff[DvtScrollbar.THUMB_TYPE] - point.y;

  if (moveDelta != 0)
    this._fireMoveEvent(true);      // fire MOVE_COMPLETE event
};

DvtScrollbar.prototype._fireMoveEvent = function(complete) {

  var eType;

  if (complete)
    eType = DvtScrollbarEvent.SB_MOVE_COMPLETE;
  else if (this._isLiveScroll)
    eType = DvtScrollbarEvent.SB_MOVE;
  else
    return;     // no live scroll, do not fire event

  var start = this._thumbStartPos - this._thumbStartMin;
  var end = start + this._thumbLength;

  var movedDelta;
  var scaledDelta;
  var sbEvent = null;

  movedDelta = this._thumbStartPos - this._lastMovePos;
  this._lastMovePos = this._thumbStartPos;
  scaledDelta = movedDelta / this._thumbLengthMax;

  if (movedDelta != 0 || complete) {
    sbEvent = new DvtScrollbarEvent(eType, this, start, end, movedDelta, scaledDelta);
  }
  if (sbEvent)
    this.FireListener(sbEvent, false);
};


DvtScrollbar.prototype._fireResizeEvent = function(complete) {

  var start = this._thumbStartPos - this._thumbStartMin;
  var end = start + this._thumbLength;
  var eType = complete ? DvtScrollbarEvent.SB_RESIZE_COMPLETE : DvtScrollbarEvent.SB_RESIZE;

  var sbEvent = new DvtScrollbarEvent(eType, this, start, end);

  this.FireListener(sbEvent, false);
};

DvtScrollbar.prototype.move = function(offsetPct, dispatchEvent, complete) {
  this.moveSliderPercent(offsetPct);
  if (dispatchEvent)
    this._fireMoveEvent(complete);      // fire MOVE_COMPLETE event
};

DvtScrollbar.prototype.moveSliderPercent = function(offsetPct) {
  if (offsetPct < -100 || offsetPct > 100)
    return;
  this.moveSlider((offsetPct / 100) * (this._thumbLengthMax));
};

DvtScrollbar.prototype.moveSlider = function(deltaPxls) {
  // The moveTo function takes care of bounds for us, so don't worry about it here
  if (this.isHorizontal())
    this.moveTo(this._thumbStartPos + deltaPxls);
  else
    this.moveTo(this._thumbStartPos + deltaPxls);
};

DvtScrollbar.prototype.processGlobalMouseUp = function() {
  this._clearTimers();
};

DvtScrollbar.prototype.getX = function() {
  return this._container.getX();
};

DvtScrollbar.prototype.getY = function() {
  return this._container.getY();
};

DvtScrollbar.prototype.getWidth = function() {
  return this._container.getWidth();
};

DvtScrollbar.prototype.getHeight = function() {
  return this._container.getHeight();
};

DvtScrollbar.prototype.getScrollbarThumb = function() {
  return this._thumb;
};

/*
  *  These have been added for users of the scrollbar in a standalone
  *  situation (no MT involved).  Essentially, the existing setThumbStartAndLength()
  *  does not work correctly or logically. The thumb sits in the scrollable area,
  *  and its initial start position should be zero (and not related to any
  *  outside chrome such as the scroll-arrows).
  */
DvtScrollbar.prototype.setLogicalThumbStartAndLength = function(start, length) {

  this._thumbStartPos = this._arrowWidth + start;
  this._thumbLength = length;

  this._thumb.updateSize();
};

DvtScrollbar.prototype.getLogicalThumbStart = function() {
  return this._thumbStartPos - this._arrowWidth;
};


/**
 * @override
 */
DvtScrollbar.prototype.destroy = function() {
  //Bug 16893087 - HTML5 HV: SEARCH RESULTS SCROLLBAR TRAPS MOUSE
  if (this._container) {
    if (this._thumb) {
      this._thumb.destroy();
      this._container.removeChild(this._thumb);
      this._thumb = null;
    }

    if (this._arrowDown) {
      this._arrowDown.destroy();
      this._container.removeChild(this._arrowDown);
      this._arrowDown = null;
    }

    if (this._arrowUp) {
      this._arrowUp.destroy();
      this._container.removeChild(this._arrowUp);
      this._arrowUp = null;
    }

    if (this._background) {
      this._background.destroy();
      this._container.removeChild(this._background);
      this._background = null;
    }
  }

  DvtScrollbar.superclass.destroy.call(this);
  this._container = null;

};

/*--------------------------------------------------------------------*/
/*   DvtScrollbarArrow          ScrollbarArrow button                 */
/*--------------------------------------------------------------------*/
/**
  *  DvtScrollbarArrow subcomponent
  *  @extends {dvt.Container}
  *  @class DvtScrollbarArrow  a subcomponent created and used
  *  by DvtScrollbar
  *  <p>
  *  @constructor
  */

var DvtScrollbarArrow = function(sb, orientation) {
  this.Init(sb, orientation);
};

dvt.Obj.createSubclass(DvtScrollbarArrow, dvt.Container, 'DvtScrollbarArrow');

// Constants
//
DvtScrollbarArrow.WIDTH = 15;   // default arrow width/height

DvtScrollbarArrow.ORIENT_RIGHT = 0;
DvtScrollbarArrow.ORIENT_UP = 1;
DvtScrollbarArrow.ORIENT_LEFT = 2;
DvtScrollbarArrow.ORIENT_DOWN = 3;

DvtScrollbarArrow.ARROW_UP_BORDER = '#000000';
DvtScrollbarArrow.ARROW_UP_BORDER_ALPHA = 0;
DvtScrollbarArrow.ARROW_UP_FILL = '#000000';
DvtScrollbarArrow.BG_UP_FILL_COLORS = ['#ffffff', '#ffffff'];
DvtScrollbarArrow.BG_UP_FILL_ALPHAS = [0, 0];
DvtScrollbarArrow.BG_UP_FILL_RATIOS = [0, 255];

DvtScrollbarArrow.ARROW_OVER_BORDER = '#000000';
DvtScrollbarArrow.ARROW_OVER_BORDER_ALPHA = 0;
DvtScrollbarArrow.ARROW_OVER_FILL = '#000000';
DvtScrollbarArrow.BG_OVER_FILL_COLORS = ['#0d8cf9', '#4ab5fc'];
DvtScrollbarArrow.BG_OVER_FILL_ALPHAS = [1, 1];
DvtScrollbarArrow.BG_OVER_FILL_RATIOS = [0, 255];

DvtScrollbarArrow.ARROW_DOWN_BORDER = '#4e555e';
DvtScrollbarArrow.ARROW_DOWN_BORDER_ALPHA = 1;
DvtScrollbarArrow.ARROW_DOWN_FILL = '#ffffff';
DvtScrollbarArrow.BG_DOWN_FILL_COLORS = ['#4e555e', '#a7b7d4'];
DvtScrollbarArrow.BG_DOWN_FILL_ALPHAS = [1, 1];
DvtScrollbarArrow.BG_DOWN_FILL_RATIOS = [0, 255];


/*--------------------------------------------------------------------*/
/*  Init()                                                            */
/*--------------------------------------------------------------------*/
/**
 *    @protected
 */
DvtScrollbarArrow.prototype.Init = function(sb, orientation)
{
  var x, y, w, h;
  this._sb = sb;
  this._orientation = orientation;
  this._arrow = null;
  this._disabled = false;

  if (sb.isHorizontal()) {
    if (orientation == DvtScrollbarArrow.ORIENT_LEFT) {
      x = sb.getX();
      y = sb.getY();
    }
    else if (orientation == DvtScrollbarArrow.ORIENT_RIGHT) {
      x = sb.getX() + sb.getWidth() - DvtScrollbarArrow.WIDTH;
      y = sb.getY();
    }
    w = DvtScrollbarArrow.WIDTH;
    h = sb.getHeight();
  }
  else {
    if (orientation == DvtScrollbarArrow.ORIENT_DOWN) {
      x = sb.getX();
      y = sb.getY() + sb.getHeight() - DvtScrollbarArrow.WIDTH;
    }
    else if (orientation == DvtScrollbarArrow.ORIENT_UP) {
      x = sb.getX();
      y = sb.getY();
    }
    w = sb.getWidth();
    h = DvtScrollbarArrow.WIDTH;

  }

  // initialize the underlying rect
  var context = sb.getCtx();
  DvtScrollbarArrow.superclass.Init.call(this, context);
  this._container = new dvt.Rect(context, x, y, w, h, null);

  this._arrowUpBorderColor = DvtScrollbarArrow.ARROW_UP_BORDER;
  this._arBGUpFillColors = DvtScrollbarArrow.BG_UP_FILL_COLORS;

  this._arrowOverBorderColor = DvtScrollbarArrow.ARROW_OVER_BORDER;
  this._arBGOverFillColors = DvtScrollbarArrow.BG_OVER_FILL_COLORS;

  this._arrowDownBorderColor = DvtScrollbarArrow.ARROW_DOWN_BORDER;
  this._arBGDownFillColors = DvtScrollbarArrow.BG_DOWN_FILL_COLORS;

  this._upFill = new dvt.LinearGradientFill(0, this._arBGUpFillColors, DvtScrollbarArrow.BG_UP_FILL_ALPHAS);
  this._overFill = new dvt.LinearGradientFill(0, this._arBGOverFillColors, DvtScrollbarArrow.BG_OVER_FILL_ALPHAS);
  this._downFill = new dvt.LinearGradientFill(0, this._arBGDownFillColors, DvtScrollbarArrow.BG_DOWN_FILL_ALPHAS);

  this._arrowUpFill = new dvt.SolidFill(DvtScrollbarArrow.ARROW_UP_FILL, null);
  this._arrowOverFill = new dvt.SolidFill(DvtScrollbarArrow.ARROW_OVER_FILL, null);
  this._arrowDownFill = new dvt.SolidFill(DvtScrollbarArrow.ARROW_DOWN_FILL, null);

  this._arrowUpBorder = new dvt.SolidStroke(DvtScrollbarArrow.ARROW_UP_BORDER, null, DvtScrollbarArrow.ARROW_UP_BORDER_ALPHA);
  this._arrowOverBorder = new dvt.SolidStroke(DvtScrollbarArrow.ARROW_OVER_BORDER, null, DvtScrollbarArrow.ARROW_OVER_BORDER_ALPHA);
  this._arrowDownBorder = new dvt.SolidStroke(DvtScrollbarArrow.ARROW_DOWN_BORDER, null, DvtScrollbarArrow.ARROW_DOWN_BORDER_ALPHA);

  this._arrow = this._createArrow();

  this._container.setPixelHinting(true);

  if (!dvt.Agent.isTouchDevice()) {
    this.addEvtListener(dvt.MouseEvent.MOUSEOVER, this._mouseOverHandler, false, this);
    this.addEvtListener(dvt.MouseEvent.MOUSEOUT, this._mouseOutHandler, false, this);
    this.addEvtListener(dvt.MouseEvent.MOUSEDOWN, this._mouseDownHandler, false, this);
    this.addEvtListener(dvt.MouseEvent.MOUSEUP, this._mouseUpHandler, false, this);
  } else {
    this.addEvtListener(dvt.TouchEvent.TOUCHSTART, this._touchHandler, false, this);
    this.addEvtListener(dvt.TouchEvent.TOUCHMOVE, this._touchHandler, false, this);
    this.addEvtListener(dvt.TouchEvent.TOUCHEND, this._touchHandler, false, this);
  }

  this._container.addChild(this._arrow);
  this.addChild(this._container);
  this._drawOutState();

};

DvtScrollbarArrow.prototype.getOrientation = function() {
  return this._orientation;
};

DvtScrollbarArrow.prototype._createArrow = function() {

  var centerX = this._container.getX() + this._container.getWidth() / 2;
  var centerY = this._container.getY() + this._container.getHeight() / 2;
  var aPoints = new Array();

  if (this._orientation == DvtScrollbarArrow.ORIENT_RIGHT) {
    aPoints.push(centerX - 2, centerY - 3);
    aPoints.push(centerX + 2, centerY);
    aPoints.push(centerX - 2, centerY + 3);
    aPoints.push(aPoints[0], aPoints[1]);
  }
  else if (this._orientation == DvtScrollbarArrow.ORIENT_UP) {
    aPoints.push(centerX - 3, centerY + 2);
    aPoints.push(centerX, centerY - 2);
    aPoints.push(centerX + 3, centerY + 2);
    aPoints.push(aPoints[0], aPoints[1]);
  }
  else if (this._orientation == DvtScrollbarArrow.ORIENT_LEFT) {
    aPoints.push(centerX + 2, centerY - 3);
    aPoints.push(centerX - 2, centerY);
    aPoints.push(centerX + 2, centerY + 3);
    aPoints.push(aPoints[0], aPoints[1]);
  }
  else if (this._orientation == DvtScrollbarArrow.ORIENT_DOWN) {
    aPoints.push(centerX - 3, centerY - 2);
    aPoints.push(centerX, centerY + 2);
    aPoints.push(centerX + 3, centerY - 2);
    aPoints.push(aPoints[0], aPoints[1]);
  }

  var shape = new dvt.Polygon(this.getCtx(), aPoints, null);
  shape.setFill(this._arrowUpFill);
  shape.setStroke(this._arrowUpBorder);
  shape.setMouseEnabled(false);

  return shape;
};

// Mouse event handling

DvtScrollbarArrow.prototype._mouseOverHandler = function(event) {
  this._drawOverState();
  this._disabled = false;

  if (event)
    event.preventDefault();
};

DvtScrollbarArrow.prototype._mouseOutHandler = function(event) {
  this._drawOutState();
  this._disabled = true;
  if (event)
    event.preventDefault();
};

DvtScrollbarArrow.prototype._mouseDownHandler = function(event) {
  this._drawDownState();
  this._sb.handleArrowDown(this, event);

  if (event)
    event.preventDefault();
};

DvtScrollbarArrow.prototype._mouseUpHandler = function(event) {
  this._drawOverState();
  this._sb.handleArrowUp(this, event);

  if (event)
    event.preventDefault();
};

DvtScrollbarArrow.prototype._touchHandler = function(event) {
  var type = event.type;
  if (type == dvt.TouchEvent.TOUCHSTART) {
    if (!this._dragStarted) {
      var touch = event.targetTouches[0];
      if (touch) {
        this._sb.processArrowDown(this);
        this._dragStarted = true;
        this._touchId = touch.identifier;
      }
    }
  } else if (type == dvt.TouchEvent.TOUCHMOVE) {
    if (this._dragStarted) {
      var touch = dvt.TouchManager.getTouchById(this._touchId, event.touches);
      if (touch) {
      }
    }
  } else if (type == dvt.TouchEvent.TOUCHEND) {
    if (this._dragStarted) {
      var touch = dvt.TouchManager.getTouchById(this._touchId, event.changedTouches);
      if (touch) {
        this._sb.processArrowUp(this);

        this._dragStarted = false;
        this._touchId = null;
      }
    }
  }
  event.blockTouchHold();
  event.preventDefault();
};

DvtScrollbarArrow.prototype._drawOutState = function() {
  this._container.setFill(this._upFill);
  this._arrow.setStroke(this._arrowUpBorder);
  this._arrow.setFill(this._arrowUpFill);
};

DvtScrollbarArrow.prototype._drawOverState = function() {
  this._container.setFill(this._overFill);
  this._arrow.setStroke(this._arrowOverBorder);
  this._arrow.setFill(this._arrowOverFill);
};

DvtScrollbarArrow.prototype._drawDownState = function() {
  this._container.setFill(this._downFill);
  this._arrow.setStroke(this._arrowDownBorder);
  this._arrow.setFill(this._arrowDownFill);
};

DvtScrollbarArrow.prototype.isDisabled = function() {
  return this._disabled;
};


/**
 * @override
 */
DvtScrollbarArrow.prototype.destroy = function() {
  //Bug 16893087 - HTML5 HV: SEARCH RESULTS SCROLLBAR TRAPS MOUSE
  if (dvt.Agent.isTouchDevice()) {
    this.removeEvtListener(dvt.TouchEvent.TOUCHSTART, this._touchHandler, false, this);
    this.removeEvtListener(dvt.TouchEvent.TOUCHMOVE, this._touchHandler, false, this);
    this.removeEvtListener(dvt.TouchEvent.TOUCHEND, this._touchHandler, false, this);
  }
  else {
    this.removeEvtListener(dvt.MouseEvent.MOUSEOVER, this._mouseOverHandler, false, this);
    this.removeEvtListener(dvt.MouseEvent.MOUSEOUT, this._mouseOutHandler, false, this);
    this.removeEvtListener(dvt.MouseEvent.MOUSEDOWN, this._mouseDownHandler, false, this);
    this.removeEvtListener(dvt.MouseEvent.MOUSEUP, this._mouseUpHandler, false, this);
  }

  if (this._container) {
    if (this._arrow) {
      this._container.removeChild(this._arrow);
      this._arrow = null;
    }
  }

  DvtScrollbarArrow.superclass.destroy.call(this);
  this._container = null;
};

/*--------------------------------------------------------------------*/
/*   DvtScrollbarBackground          Scrollbar background             */
/*--------------------------------------------------------------------*/
/**
  *  DvtScrollbarBackground subcomponent
  *  @extends {dvt.Container}
  *  @class DvtScrollbarBackground  a subcomponent created and used
  *  by DvtScrollbar
  *  <p>
  *  @constructor
  */

var DvtScrollbarBackground = function(sb) {
  this.Init(sb);
};

dvt.Obj.createSubclass(DvtScrollbarBackground, dvt.Container, 'DvtScrollbarBackground');

// Constants
//
DvtScrollbarBackground.BORDER_COLOR = 'rgb(206,211,226)';
DvtScrollbarBackground.FILL_COLORS = ['#fafafb', '#e7e9ef'];


/*--------------------------------------------------------------------*/
/*  Init()                                                            */
/*--------------------------------------------------------------------*/
/**
 *    @private
 */
DvtScrollbarBackground.prototype.Init = function(sb)
{
  var x, y, w, h;
  this._sb = sb;

  x = sb.getX();
  y = sb.getY();
  w = sb.getWidth();
  h = sb.getHeight();

  // create and initialize the underlying rect
  var context = sb.getCtx();
  DvtScrollbarBackground.superclass.Init.call(this, context);
  this._container = new dvt.Rect(context, x, y, w, h, null);
  this.addChild(this._container);

  var fill;
  var styleMap = sb.getStyleMap();
  var background;
  var borderColor;
  if (styleMap) {
    background = styleMap[DvtScrollbar.BACKGROUND];
    borderColor = styleMap[DvtScrollbar.BORDER_COLOR];
    if (background) {
      if (dvt.ColorUtils.isColor(background))
        fill = new dvt.SolidFill(background);
    } else {
      var grad = dvt.GradientParser.parseCSSGradient(background);
      if (grad)
        fill = new dvt.LinearGradientFill(grad.getAngle(), grad.getColors(), grad.getAlphas(), grad.getRatios());
    }
  }

  if (!fill) {
    var gradientAngle;
    if (sb.isHorizontal())
      gradientAngle = 90;
    else
      gradientAngle = 180;
    fill = new dvt.LinearGradientFill(gradientAngle, DvtScrollbarBackground.FILL_COLORS);
  }

  this._container.setSolidStroke(borderColor ? borderColor : DvtScrollbarBackground.BORDER_COLOR);
  this._container.setFill(fill);

  this._container.setPixelHinting(true);

  if (!dvt.Agent.isTouchDevice()) {
    this.addEvtListener(dvt.MouseEvent.CLICK, this._mouseClickHandler, false, this);
  } else {
    this.addEvtListener(dvt.TouchEvent.TOUCHSTART, this._touchHandler, false, this);
    this.addEvtListener(dvt.TouchEvent.TOUCHMOVE, this._touchHandler, false, this);
    this.addEvtListener(dvt.TouchEvent.TOUCHEND, this._touchHandler, false, this);
  }
};

DvtScrollbarBackground.prototype._touchHandler = function(event) {
  var type = event.type;
  if (type == dvt.TouchEvent.TOUCHSTART) {
    if (!this._backgroundClickStarted) {
      var touch = event.targetTouches[0];
      if (touch) {
        this._backgroundClickStarted = true;
        this._touchId = touch.identifier;
      }
    }
  } else if (type == dvt.TouchEvent.TOUCHMOVE) {
  } else if (type == dvt.TouchEvent.TOUCHEND) {
    if (this._backgroundClickStarted) {
      var touch = dvt.TouchManager.getTouchById(this._touchId, event.changedTouches);
      if (touch) {
        var pos = this.getCtx().pageToStageCoords(touch.pageX, touch.pageY);
        var point = new dvt.Point(pos.x, pos.y);
        this._sb.processBackgroundClick(point);

        this._backgroundClickStarted = false;
        this._touchId = null;
      }
    }
  }
  event.blockTouchHold();
  event.preventDefault();
};

DvtScrollbarBackground.prototype._mouseClickHandler = function(event) {
  var pos = this.getCtx().pageToStageCoords(event._event.pageX, event._event.pageY);
  var point = new dvt.Point(pos.x, pos.y);
  this._sb.processBackgroundClick(point);

  if (event)
    event.preventDefault();
};


/**
 * @override
 */
DvtScrollbarBackground.prototype.destroy = function() {
  //Bug 16893087 - HTML5 HV: SEARCH RESULTS SCROLLBAR TRAPS MOUSE
  if (dvt.Agent.isTouchDevice()) {
    this.removeEvtListener(dvt.TouchEvent.TOUCHSTART, this._touchHandler, false, this);
    this.removeEvtListener(dvt.TouchEvent.TOUCHMOVE, this._touchHandler, false, this);
    this.removeEvtListener(dvt.TouchEvent.TOUCHEND, this._touchHandler, false, this);
  }
  else {
    this.removeEvtListener(dvt.MouseEvent.CLICK, this._mouseClickHandler, false, this);
  }

  DvtScrollbarBackground.superclass.destroy.call(this);
  this._container = null;

};

/**
  *  DvtScrollbarThumb subcomponent
  *  @param {DvtScrollbar} sb
  *  @param {string} skinName
  *  @extends {dvt.Container}
  *  @class DvtScrollbarThumb  a subcomponent created and used by DvtScrollbar
  *  @constructor
  */
var DvtScrollbarThumb = function(sb, skinName) {
  this.Init(sb, skinName);
};

dvt.Obj.createSubclass(DvtScrollbarThumb, dvt.Container, 'DvtScrollbarThumb');

DvtScrollbarThumb.UP_BORDER_COLOR = '#c2cadb';
DvtScrollbarThumb.UP_FILL_COLORS = ['#ffffff', '#e4eef8'];
DvtScrollbarThumb.UP_FILL_ALPHAS = [1, 1];
DvtScrollbarThumb.UP_FILL_RATIOS = [70, 255];

DvtScrollbarThumb.OVER_BORDER_COLOR = '#097bf8';
DvtScrollbarThumb.OVER_FILL_COLORS = ['#ffffff', '#bfdefd'];
DvtScrollbarThumb.OVER_FILL_ALPHAS = [1, 1];
DvtScrollbarThumb.OVER_FILL_RATIOS = [70, 255];

DvtScrollbarThumb.DOWN_BORDER_COLOR = '#c2cadb';
DvtScrollbarThumb.DOWN_FILL_COLORS = ['#ffffff', '#e4eef8'];
DvtScrollbarThumb.DOWN_FILL_ALPHAS = [1, 1];
DvtScrollbarThumb.DOWN_FILL_RATIOS = [70, 255];

DvtScrollbarThumb.GRIP_ZONE_COLOR = '#b6bbca';


/**
 *  @param {DvtScrollbar} sb
 *  @param {string} skinName
 */
DvtScrollbarThumb.prototype.Init = function(sb, skinName)
{
  var x, y, w, h;
  this._sb = sb;
  this._skinName = skinName;

  if (sb.isHorizontal()) {
    x = sb.getX() + sb.getThumbStart();
    y = sb.getY();
    w = sb.getThumbLength();
    h = sb.getHeight();
  }
  else {
    x = sb.getX();
    y = sb.getY() + sb.getHeight() - sb.getThumbStart() - sb.getThumbLength();
    w = sb.getWidth();
    h = sb.getThumbLength();
  }

  // create and initialize the underlying rect
  var context = sb.getCtx();
  DvtScrollbarThumb.superclass.Init.call(this, context);
  this._container = new dvt.Rect(context, x, y, w, h, null);

  var angle = (this._sb.isHorizontal()) ? 270 : 0;

  var styleMap = sb.getStyleMap();
  if (this.IsSkinAlta() && styleMap) {
    this._upFill = new dvt.SolidFill(styleMap[DvtScrollbar.HANDLE_COLOR]);
    this._overFill = new dvt.SolidFill(styleMap[DvtScrollbar.HANDLE_HOVER_COLOR]);
    this._downFill = new dvt.SolidFill(styleMap[DvtScrollbar.HANDLE_ACTIVE_COLOR]);
    this._upStroke = null;
    this._overStroke = null;
    this._downStroke = null;
  }
  else {
    this._upFill = new dvt.LinearGradientFill(angle, DvtScrollbarThumb.UP_FILL_COLORS, DvtScrollbarThumb.UP_FILL_ALPHAS);
    this._overFill = new dvt.LinearGradientFill(angle, DvtScrollbarThumb.OVER_FILL_COLORS, DvtScrollbarThumb.OVER_FILL_ALPHAS);
    this._downFill = new dvt.LinearGradientFill(angle, DvtScrollbarThumb.DOWN_FILL_COLORS, DvtScrollbarThumb.DOWN_FILL_ALPHAS);
    this._upStroke = new dvt.SolidStroke(DvtScrollbarThumb.UP_BORDER_COLOR, null, 1);
    this._overStroke = new dvt.SolidStroke(DvtScrollbarThumb.OVER_BORDER_COLOR, null, 1);
    this._downStroke = new dvt.SolidStroke(DvtScrollbarThumb.DOWN_BORDER_COLOR, null, 1);
    this._grip = this._createGrip();
    this._container.addChild(this._grip);
  }

  if (!dvt.Agent.isTouchDevice()) {
    this.addEvtListener(dvt.MouseEvent.MOUSEOVER, this._mouseOverHandler, false, this);
    this.addEvtListener(dvt.MouseEvent.MOUSEOUT, this._mouseOutHandler, false, this);
    this._removeDragListeners = dvt.SvgDocumentUtils.addDragListeners(this, this._mouseDownHandler, this._mouseMoveHandler, this._mouseUpHandler, this);
  } else {
    this.addEvtListener(dvt.TouchEvent.TOUCHSTART, this._touchHandler, false, this);
    this.addEvtListener(dvt.TouchEvent.TOUCHMOVE, this._touchHandler, false, this);
    this.addEvtListener(dvt.TouchEvent.TOUCHEND, this._touchHandler, false, this);
  }

  this._container.setPixelHinting(true);
  this.addChild(this._container);

  if (!dvt.Agent.isTouchDevice()) {
    this._drawUpState();
  } else {
    this._drawOverState();
  }

};

DvtScrollbarThumb.prototype.IsSkinAlta = function() {
  return (this._skinName == 'alta');
};


/**
 * Processes "touchstart" event
 * @param {dvt.TouchEvent} event Touch event
 * @param {dvt.Touch} touch Touch object
 */
DvtScrollbarThumb.prototype.immediateTouchAttempted = function(event, touch) {
  // grippers are children of the thumb so this check is needed
  if (this.getTouchManager().containsTouchId(touch.identifier)) {
    return;
  }
  var point = this.getCtx().pageToStageCoords(touch.pageX, touch.pageY);
  this.processThumbDragStart(point);
  this.getTouchManager().saveProcessedTouchDefault(touch.identifier, this, this._container, this._sb, 'thumb', this);

  this.drawHighlightState();

  event.blockTouchHold();
};


/**
 * Processes "touchmove" event
 * @param {dvt.TouchEvent} event Touch event
 * @param {dvt.Touch} touch Touch object
 */
DvtScrollbarThumb.prototype.immediateTouchMoved = function(event, touch) {
  var point = this.getCtx().pageToStageCoords(touch.pageX, touch.pageY);
  this.processThumbDragMove(point);
  event.preventDefault();
};


/**
 * Processes "touchend" event
 * @param {dvt.TouchEvent} event Touch event
 * @param {dvt.Touch} touch Touch object
 */
DvtScrollbarThumb.prototype.immediateTouchEnded = function(event, touch) {
  if (!this.getTouchManager().containsGrouping(this._sb)) {
    this.drawPlainState();
  }
  var point = this.getCtx().pageToStageCoords(touch.pageX, touch.pageY);
  this.processThumbDragEnd(point);
};


/**
 * Handler for the touch event
 * @param {dvt.TouchEvent} event Touch event
 * @private
 */
DvtScrollbarThumb.prototype._touchHandler = function(event) {
  this.getTouchManager().preEventBubble(event);
  this.getTouchManager().processAssociatedTouchDefault(event, this);
  this.getTouchManager().postEventBubble(event);
};

DvtScrollbarThumb.prototype.updateSize = function() {
  var sb = this._sb;

  if (sb.isHorizontal()) {
    this.setX(sb.getX() + sb.getThumbStart());
    this.setWidth(sb.getThumbLength());
  }
  else {
    // this.setY(sb.getY() + sb.getHeight() - sb.getThumbStart() - sb.getThumbLength());
    this.setY(sb.getY() + sb.getThumbStart());
    this.setHeight(sb.getThumbLength());
  }

  if (this._grip != null && this._container._findChild(this._grip) != -1) {
    this._container.removeChild(this._grip);
    this._grip = this._createGrip();
    this._container.addChild(this._grip);
  }
  else {
    this._grip = this._createGrip();
  }
};


DvtScrollbarThumb.prototype.isHorizontal = function() {
  return this._sb.isHorizontal();
};

DvtScrollbarThumb.prototype._createGrip = function() {

  var centerX = this.getX() + this.getWidth() / 2;
  var centerY = this.getY() + this.getHeight() / 2;
  var w = 3;      // half of grip width

  var gripCmds = '';

  if (this._sb.isHorizontal()) {
    gripCmds += dvt.PathUtils.moveTo(centerX - 4.5, centerY - w);
    gripCmds += dvt.PathUtils.lineTo(centerX - 4.5, centerY + w);
    gripCmds += dvt.PathUtils.moveTo(centerX - 1.5, centerY - w);
    gripCmds += dvt.PathUtils.lineTo(centerX - 1.5, centerY + w);
    gripCmds += dvt.PathUtils.moveTo(centerX + 1.5, centerY - w);
    gripCmds += dvt.PathUtils.lineTo(centerX + 1.5, centerY + w);
    gripCmds += dvt.PathUtils.moveTo(centerX + 4.5, centerY - w);
    gripCmds += dvt.PathUtils.lineTo(centerX + 4.5, centerY + w);
  }
  else {
    gripCmds += dvt.PathUtils.moveTo(centerX - w, centerY - 4.5);
    gripCmds += dvt.PathUtils.lineTo(centerX + w, centerY - 4.5);
    gripCmds += dvt.PathUtils.moveTo(centerX - w, centerY - 1.5);
    gripCmds += dvt.PathUtils.lineTo(centerX + w, centerY - 1.5);
    gripCmds += dvt.PathUtils.moveTo(centerX - w, centerY + 1.5);
    gripCmds += dvt.PathUtils.lineTo(centerX + w, centerY + 1.5);
    gripCmds += dvt.PathUtils.moveTo(centerX - w, centerY + 4.5);
    gripCmds += dvt.PathUtils.lineTo(centerX + w, centerY + 4.5);
  }

  dvt.PathUtils.closePath();
  var shape = new dvt.Path(this.getCtx(), gripCmds);
  shape.setSolidStroke(DvtScrollbarThumb.GRIP_ZONE_COLOR);
  shape.setMouseEnabled(false);

  return shape;
};

// Mouse event handlers

DvtScrollbarThumb.prototype._mouseOverHandler = function(event) {
  this._drawOverState();
  if (event)
    event.preventDefault();
};


DvtScrollbarThumb.prototype._mouseOutHandler = function(event) {
  this._drawUpState();

  if (event)
    event.preventDefault();
};


/**
 * Handles a mouse down event for this scrollbar thumb.
 * @param {dvt.MouseEvent} event The mouse event to handle
 * @return {boolean} true in order to work correctly with dvt.SvgDocumentUtils.addDragListeners
 * @private
 */
DvtScrollbarThumb.prototype._mouseDownHandler = function(event) {

  // remove 'mouseover' and 'mouseout' listeners while dragging
  this.removeEvtListener(dvt.MouseEvent.MOUSEOVER, this._mouseOverHandler, false, this);
  this.removeEvtListener(dvt.MouseEvent.MOUSEOUT, this._mouseOutHandler, false, this);

  var point = new dvt.Point(event.pageX, event.pageY);
  this.processThumbDragStart(point);
  this._drawDownState();
  return true;
};

DvtScrollbarThumb.prototype.processThumbDragStart = function(point) {
  this._sb.processThumbDragStart(point);
};


/**
 * Handles a mouse up event for this scrollbar thumb.
 * @param {dvt.MouseEvent} event The event to handle
 * @private
 */
DvtScrollbarThumb.prototype._mouseUpHandler = function(event) {
  var point = new dvt.Point(event.pageX, event.pageY);
  this.processThumbDragEnd(point);
  this._drawUpState();

  // restore 'mouseover' and 'mouseout' listeners
  this.addEvtListener(dvt.MouseEvent.MOUSEOVER, this._mouseOverHandler, false, this);
  this.addEvtListener(dvt.MouseEvent.MOUSEOUT, this._mouseOutHandler, false, this);

  if (event)
    event.preventDefault();
};

DvtScrollbarThumb.prototype.processThumbDragEnd = function(point) {
  this._sb.processThumbDragEnd(point);
};


/**
 * Handles a mouse move event for this scrollbar thumb.
 * @param {dvt.MouseEvent} event The mouse event to handle
 * @private
 */
DvtScrollbarThumb.prototype._mouseMoveHandler = function(event) {

  var point = new dvt.Point(event.pageX, event.pageY);

  this.processThumbDragMove(point);

  if (event)
    event.preventDefault();
};

DvtScrollbarThumb.prototype.processThumbDragMove = function(point) {
  this._sb.processThumbDragMove(point);
};

DvtScrollbarThumb.prototype._drawUpState = function() {
  this.drawPlainState();
};

DvtScrollbarThumb.prototype.drawHighlightState = function() {
  if (this._overStroke)
    this.setStroke(this._overStroke);
  this.setFill(this._overFill);
};

DvtScrollbarThumb.prototype.drawPlainState = function() {
  if (this._upStroke)
    this.setStroke(this._upStroke);
  this.setFill(this._upFill);
};

DvtScrollbarThumb.prototype._drawOverState = function() {
  if (!dvt.Agent.isTouchDevice()) {
    this.drawHighlightState();
  } else {
    this.drawPlainState();
  }
};

DvtScrollbarThumb.prototype._drawDownState = function() {
  if (this._downStroke)
    this.setStroke(this._downStroke);
  this.setFill(this._downFill);
};

DvtScrollbarThumb.prototype.getX = function() {
  return this._container.getX();
};

DvtScrollbarThumb.prototype.getY = function() {
  return this._container.getY();
};

DvtScrollbarThumb.prototype.getWidth = function() {
  return this._container.getWidth();
};

DvtScrollbarThumb.prototype.getHeight = function() {
  return this._container.getHeight();
};

DvtScrollbarThumb.prototype.setX = function(val) {
  this._container.setX(val);
};

DvtScrollbarThumb.prototype.setY = function(val) {
  this._container.setY(val);
};

DvtScrollbarThumb.prototype.setWidth = function(val) {
  this._container.setWidth(val);
};

DvtScrollbarThumb.prototype.setHeight = function(val) {
  this._container.setHeight(val);
};

DvtScrollbarThumb.prototype.setStroke = function(stroke) {
  this._container.setStroke(stroke);
};

DvtScrollbarThumb.prototype.setSolidStroke = function(color, alpha, strokeWidth) {
  this._container.setSolidStroke(color, alpha, strokeWidth);
};

DvtScrollbarThumb.prototype.setFill = function(fill) {
  this._container.setFill(fill);
};

DvtScrollbarThumb.prototype.setSolidFill = function(color, alpha) {
  this._container.setSolidFill(color, alpha);
};


/**
 * @override
 */
DvtScrollbarThumb.prototype.destroy = function() {
  //Bug 16893087 - HTML5 HV: SEARCH RESULTS SCROLLBAR TRAPS MOUSE
  if (dvt.Agent.isTouchDevice()) {
    this.removeEvtListener(dvt.TouchEvent.TOUCHSTART, this._touchHandler, false, this);
    this.removeEvtListener(dvt.TouchEvent.TOUCHMOVE, this._touchHandler, false, this);
    this.removeEvtListener(dvt.TouchEvent.TOUCHEND, this._touchHandler, false, this);
  }
  else {
    this.removeEvtListener(dvt.MouseEvent.MOUSEOVER, this._mouseOverHandler, false, this);
    this.removeEvtListener(dvt.MouseEvent.MOUSEOUT, this._mouseOutHandler, false, this);
    this.removeEvtListener(dvt.MouseEvent.MOUSEDOWN, this._mouseDownHandler, false, this);
    this._removeDragListeners.call();

    this.getCtx().getStage().removeEvtListener(dvt.MouseEvent.MOUSEMOVE, this._mouseMoveHandler, false, this);
    this.getCtx().getStage().removeEvtListener(dvt.MouseEvent.MOUSEUP, this._mouseUpHandler, false, this);
  }

  if (this._container) {
    if (this._grip) {
      this._container.removeChild(this._grip);
      this._grip = null;
    }
  }

  DvtScrollbarThumb.superclass.destroy.call(this);
  this._container = null;

};


/**
 * Creates if necessary and returns a touch manager object
 * @return {dvt.TouchManager} touch manager object for the scrollbar thumb
 */
DvtScrollbarThumb.prototype.getTouchManager = function() {
  if (!this._touchManager && dvt.Agent.isTouchDevice())
    this._touchManager = new dvt.TouchManager('_touchManager_sb', this._context);
  return this._touchManager;
};
// Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved.
/**
 * A transparent container that adds vertical and horizontal scrollbars as needed
 * @extends {dvt.Container}
 * @constructor
 * @param {dvt.Context} context
 * @param {number}     minW    The min width of the scrollable container used for getDimensions.
 * @param {number}     minH    The min height of the scrollable container used for getDimensions.
 * @param {number}     maxW    The max width of the scrollable container. If null, no clipping will be performed.
 * @param {number}     maxH    The max height of the scrollable container. If null, no clipping will be performed.
 * @param {number} gap The gap between the scrollbar and the content
 * @param {string} skinName The skin for this container
 */
var DvtScrollableContainer = function(context, minW, minH, maxW, maxH, gap, skinName) {
  this.Init(context, minW, minH, maxW, maxH, gap, skinName);
};

dvt.Obj.createSubclass(DvtScrollableContainer, dvt.Container, 'DvtScrollableContainer');

DvtScrollableContainer.DEFAULT_SCROLLBAR_WIDTH = 14;// thickness of scrollbar
DvtScrollableContainer.DEFAULT_ALTA_SCROLLBAR_WIDTH = 11;


/**
 * Initializer for this object
 * @param {dvt.Context} context The platform specific context object
 * @param {number} minW The minimum width for this container
 * @param {number} minH The minimum height for this container
 * @param {number} maxW The maximum width for this container
 * @param {number} maxH The maximum height for this container
 * @param {number} gap The gap between the scrollbar and the content
 * @param {string} skinName The skin for this container
 * @protected
 */
DvtScrollableContainer.prototype.Init = function(context, minW, minH, maxW, maxH, gap, skinName) {
  DvtScrollableContainer.superclass.Init.call(this, context);
  this._scrollbarContentGap = gap;
  this._skinName = skinName;
  if (this.IsSkinAlta())
    this._sbWidth = DvtScrollableContainer.DEFAULT_ALTA_SCROLLBAR_WIDTH;
  else
    this._sbWidth = DvtScrollableContainer.DEFAULT_SCROLLBAR_WIDTH;

  if (maxW) {
    this._maxW = maxW;
    this._maxContentW = maxW - this._sbWidth - this._scrollbarContentGap;
  } else {
    this._maxW = Number.MAX_VALUE;
    this._maxContentW = Number.MAX_VALUE;
  }

  if (maxH) {
    this._maxH = maxH;
    this._maxContentH = maxH - this._sbWidth - this._scrollbarContentGap;
  } else {
    this._maxH = Number.MAX_VALUE;
    this._maxContentH = Number.MAX_VALUE;
  }

  this._minW = minW;
  this._fullW = 0;// current contentPane width

  this._minH = minH;
  this._fullH = 0;// current contentPane height


  this._hsb = null;// horiz scrollbar obj
  this._hsbScale = 0;
  this._hsbStart = 0;

  this._vsb = null;// vert scriollbar obj
  this._vsbScale = 0;
  this._vsbStart = 0;

  this._translateY = 0;
  this._translateX = 0;
  this._contentPane = new dvt.Container(context); // user adds objs to this
  // Want to add content pane to DvtScrollableContainer
  DvtScrollableContainer.superclass.addChild.call(this, this._contentPane);

  this._isBiDi = dvt.Agent.isRightToLeft(context);
};

DvtScrollableContainer.prototype.getSkinName = function() {
  return this._skinName;
};

DvtScrollableContainer.prototype.IsSkinAlta = function() {
  return (this._skinName == 'alta');
};

DvtScrollableContainer.prototype.setStyleMap = function(styleMap) {
  this._styleMap = styleMap;
};

/**
 * Adds an object to the scrollable container and updates the container to include scrolling as necessary
 *  @param {dvt.Obj} obj The object to be added
 *  @override
 */
DvtScrollableContainer.prototype.addChild = function(obj) {
  this._contentPane.addChild(obj);
  this._addScrollEventListener(obj);
  this.refresh();
};


/**
 * Adds an object to the scrollable container at the specified index and updates the container to include scrolling as necessary
 *  @param {dvt.Obj} obj The object to be added
 *  @param {number} index The index at which to add this object
 *  @override
 */
DvtScrollableContainer.prototype.addChildAt = function(obj, index) {
  this._contentPane.addChildAt(obj, index);
  this._addScrollEventListener(obj);
  this.refresh();
};


/**
 * Removes an object from the scrollable container and updates the container to remove scrolling as necessary
 *  @param {dvt.Obj} obj The object to be removed
 *  @override
 */
DvtScrollableContainer.prototype.removeChild = function(obj) {
  this._removeScrollEventListener(obj);
  this._contentPane.removeChild(obj);
  this.refresh();
};


/**
 * Removes an object from the scrollable container at the specified index and updates the container to remove scrolling as necessary
 *  @param {number} index The index at which to add this object
 *  @override
 */
DvtScrollableContainer.prototype.removeChildAt = function(idx) {
  var obj = this._contentPane.getChildAt(idx);
  this._removeScrollEventListener(obj);
  this._contentPane.removeChildAt(idx);
  this.refresh();
};


/**
 * @override
 */
DvtScrollableContainer.prototype.getNumChildren = function() {
  return this._contentPane.getNumChildren();
};


/**
 * @override
 */
DvtScrollableContainer.prototype.contains = function(obj) {
  return this._contentPane.contains(obj);
};


/**
 * @override
 */
DvtScrollableContainer.prototype.getChildAfter = function(obj) {
  return this._contentPane.getChildAfter(obj);
};


/**
 * @override
 */
DvtScrollableContainer.prototype.getChildBefore = function(obj) {
  return this._contentPane.getChildBefore(obj);
};


/**
 * @override
 */
DvtScrollableContainer.prototype.getChildAt = function(idx) {
  return this._contentPane.getChildAt(idx);
};


/**
 * @override
 */
DvtScrollableContainer.prototype.getChildIndex = function(obj) {
  return this._contentPane.getChildIndex(obj);
};


/**
 * @override
 */
DvtScrollableContainer.prototype.removeChildren = function() {
  var numChildren = this._contentPane.getNumChildren();
  for (var childIndex = 0; childIndex < numChildren; childIndex++) {
    this._removeScrollEventListener(this._contentPane.getChildAt(childIndex));
  }
  this._contentPane.removeChildren();
};


/**
 * @override
 */
DvtScrollableContainer.prototype.destroy = function() {
  DvtScrollableContainer.superclass.destroy.call(this);
  // content pane's children have already been destroyed, just need to destroy content pane
  this._contentPane.setClipPath(null);
  this._contentPane.removeAllDrawEffects(null);
  this._removeHSB();
  this._removeVSB();
};

/**
 * @override
 */
DvtScrollableContainer.prototype.getDimensions = function(targetCoordinateSpace) {
  // Bug 16815388 - HTML5 HV: TWO SCROLLBARS WERE DISPLAYED AFTER SELECTING A SEARCH RESULT
  var dims = this._getDimensionsForContentPane(false, this);
  dims = this._getDimensionsWithScrollbar(dims);
  return targetCoordinateSpace && dims ? this.ConvertCoordSpaceRect(dims, targetCoordinateSpace) : dims;
};

/**
 * @override
 */
DvtScrollableContainer.prototype.getDimensionsWithStroke = function(targetCoordinateSpace) {
  // Bug 16815388 - HTML5 HV: TWO SCROLLBARS WERE DISPLAYED AFTER SELECTING A SEARCH RESULT
  var dims = this._getDimensionsForContentPane(true, this);
  dims = this._getDimensionsWithScrollbar(dims);
  return targetCoordinateSpace && dims ? this.ConvertCoordSpaceRect(dims, targetCoordinateSpace) : dims;
};

/**
 * @private
 * Get container dimensions including scrollbars
 * @param {dvt.Rectangle} dims content pane dimensions
 * @return {dvt.Rectangle} container dimensions with scrollbars
 */
DvtScrollableContainer.prototype._getDimensionsWithScrollbar = function(dims) {
  if (dims) {
    if (this.hasHorizontalScrollbar())
      dims.h = Math.min(this._maxContentH, dims.h) + this._scrollbarContentGap + this._sbWidth;
    if (this.hasVerticalScrollbar())
      dims.w = Math.min(this._maxContentW, dims.w) + this._scrollbarContentGap + this._sbWidth;

    dims.x = 0;
    dims.y = 0;
    dims.w = Math.min(this._maxW, dims.w);
    dims.h = Math.min(this._maxH, dims.h);
  }
  return dims;
};

DvtScrollableContainer.prototype.setVerticalThumbPosition = function(start) {
  if (this._vsb) {
    this._vsbStart = start;
    this._contentPane.setTranslateY(-start);
    this._vsb.moveSlider(start - this._vsb.getThumbStart());
  }
};

/**
 * Set vertical position of the content
 * @param {number} ty  content vertical position in pixels
 */
DvtScrollableContainer.prototype.setVerticalContentPosition = function(ty) {
  if (this._vsb) {
    var deltaY = (ty - this._translateY) / this._vsbScale;
    //Move Slider in opposite direction
    this._vsb.moveSlider(-deltaY);
  }
  //Move Content
  this._translateY = ty;
  this._contentPane.setTranslateY(this._translateY);
};

/**
 * Get vertical position of the content
 * @return {number}  content vertical position in pixels
 */
DvtScrollableContainer.prototype.getVerticalContentPosition = function() {
  return this._contentPane.getTranslateY();
};

DvtScrollableContainer.prototype.getVerticalThumbPosition = function() {
  return this._vsb.getThumbStart();
};


/**
 *  Returns the visibility state of the horizontal scrollbar.
 *  @return {boolean} true if the horizontal scrollbar is visible, else false.
 */
DvtScrollableContainer.prototype.hasHorizontalScrollbar = function() {
  return (this._hsb != null);
};


/**
 * Checks the current dimensions of the objects added to the scrollable container and adds scrollbars as needed.
 */
DvtScrollableContainer.prototype.refresh = function() {
  this._removeVSB();
  this._removeHSB();

  var addVSB = false;
  var addHSB = false;
  //Bug 16815388 - HTML5 HV: TWO SCROLLBARS WERE DISPLAYED AFTER SELECTING A SEARCH RESULT
  var dim = this._getDimensionsForContentPane(false);
  if (dim) {
    if ((dim.x + dim.w) > this._maxW)
      addHSB = true;
    if ((dim.y + dim.h) > (addHSB ? this._maxContentH : this._maxH))
      addVSB = true;

    this._fullH = Math.max(dim.y + dim.h, this._minH);
    this._fullW = Math.max(dim.x + dim.w, this._minW);
  } else {
    this._fullH = this._minH;
    this._fullW = this._minW;
  }

  var cp = new dvt.ClipPath();
  if (addVSB && addHSB)
    cp.addRect(this._isBiDi ? this._scrollbarContentGap + this._sbWidth : 0, 0, this._maxContentW, this._maxContentH);
  else if (addVSB)
    cp.addRect(this._isBiDi ? this._scrollbarContentGap + this._sbWidth : 0, 0, this._maxContentW, this._maxH);
  else if (addHSB)
    cp.addRect(0, 0, this._maxW, this._maxContentH);
  else
    cp = null;
  this._contentPane.setClipPath(cp);


  if (addVSB)
    this._addVSB(addHSB);

  if (addHSB)
    this._addHSB(addVSB);

  if (this._isBiDi && addVSB)
    this._translateX = addHSB ? -(this._fullW - this._maxW) : this._sbWidth + this._scrollbarContentGap;
  else
    this._translateX = 0;
  this._contentPane.setTranslateX(this._translateX);
};


/**
 *  Returns the visibility state of the vertical scrollbar.
 *  @return {boolean} true if the vertical scrollbar is visible, else false.
 */
DvtScrollableContainer.prototype.hasVerticalScrollbar = function() {
  return (this._vsb != null);
};

DvtScrollableContainer.prototype._removeHSB = function() {
  if (this._hsb) {
    this._hsb.removeEvtListener(DvtScrollbarEvent.SB_MOVE, this._onHorizontalScroll, false, this);
    this._hsb.destroy();
    DvtScrollableContainer.superclass.removeChild.call(this, this._hsb);
    this._hsb = null;
  }
};

DvtScrollableContainer.prototype._removeVSB = function() {
  if (this._vsb) {
    this._vsb.removeEvtListener(DvtScrollbarEvent.SB_MOVE, this._onVerticalScroll, false, this);
    this._vsb.destroy();
    DvtScrollableContainer.superclass.removeChild.call(this, this._vsb);
    this._vsb = null;
  }
};


/**
 * Creates a vertical scrollbar for this scrollable container
 * @param {boolean} hasHSB Whether there is a horizontal scrollbar
 * @private
 */
DvtScrollableContainer.prototype._addVSB = function(hasHSB) {
  if (!this._vsb) {
    var scrollbarLength = hasHSB ? this._maxH - this._sbWidth : this._maxH;
    this._vsbLength = scrollbarLength;
    var x = this._isBiDi ? 0 : (hasHSB ? this._maxW - this._sbWidth : this._fullW + this._scrollbarContentGap);
    var width = this._sbWidth;
    var height = scrollbarLength;
    var y = 0;
    // if just vsb and is alta, adjust width/height/y so the scrollbar top, bottom, and right borders gets clipped
    var clip;
    if (this.IsSkinAlta() && !hasHSB) {
      width += 1;
      height += 2;
      y -= 1;
      clip = new dvt.ClipPath('vsb');
      clip.addRect(x, 0, width - 1, height - 2);
    }
    this._vsb = new DvtScrollbar(this.getCtx(), x, y, width, height, false, this.getSkinName(), this._styleMap);
    if (clip)
      this._vsb.setClipPath(clip);
    // Want to add scrollbar to DvtScrollableContainer, not content pane
    DvtScrollableContainer.superclass.addChild.call(this, this._vsb);
  }

  var viewableContentH = hasHSB ? this._maxContentH : this._maxH;
  var neededThumbLength = (viewableContentH / this._fullH) * this._vsb.getScrollAreaLength();
  // constrain thumb length to max and min available thumb size
  var thumbLength = Math.min(this._vsb.getScrollAreaLength(), Math.max(neededThumbLength, DvtScrollbar.MIN_THUMB_SIZE));

  this._vsbScale = (this._fullH - viewableContentH) / (this._vsb.getScrollAreaLength() - thumbLength);
  this._vsb.setLogicalThumbStartAndLength(this._vsbStart, thumbLength);
  this._vsb.addEvtListener(DvtScrollbarEvent.SB_MOVE, this._onVerticalScroll, false, this);
};


/**
 * Creates a horizontal scrollbar for this scrollable container
 * @param {boolean} hasVSB Whether there is a vertical scrollbar
 * @private
 */
DvtScrollableContainer.prototype._addHSB = function(hasVSB) {
  if (!this._hsb) {
    var scrollbarLength = hasVSB ? this._maxW - this._sbWidth : this._maxW;
    this._hsbLength = scrollbarLength;
    var x = (this._isBiDi && hasVSB) ? this._sbWidth : 0;
    var y = hasVSB ? (this._maxH - this._sbWidth) : (this._fullH + this._scrollbarContentGap);

    var height = this._sbWidth;
    var width = scrollbarLength;
    // if just hsb and is alta, adjust width/height so the scrollbar bottom, left, and right borders get clipped
    var clip;
    if (this.IsSkinAlta() && !hasVSB) {
      height += 1;
      width += 2;
      clip = new dvt.ClipPath('hsb');
      clip.addRect(x, y, width - 2, height - 1);
    }

    this._hsb = new DvtScrollbar(this.getCtx(), x, y, width, height, true, this.getSkinName(), this._styleMap);
    if (clip)
      this._hsb.setClipPath(clip);
    // Want to add scrollbar to DvtScrollableContainer, not content pane
    DvtScrollableContainer.superclass.addChild.call(this, this._hsb);
  }

  var viewableContentW = hasVSB ? this._maxContentW : this._maxW;
  var neededThumbLength = (viewableContentW / this._fullW) * this._hsb.getScrollAreaLength();
  // constrain thumb length to max and min available thumb size
  var thumbLength = Math.min(this._hsb.getScrollAreaLength(), Math.max(neededThumbLength, DvtScrollbar.MIN_THUMB_SIZE));
  this._hsbScale = (this._fullW - viewableContentW) / (this._hsb.getScrollAreaLength() - thumbLength);
  if (this._isBiDi)
    this._hsbStart = this._hsb.getScrollAreaLength() - thumbLength;
  this._hsb.setLogicalThumbStartAndLength(this._hsbStart, thumbLength);
  this._hsb.addEvtListener(DvtScrollbarEvent.SB_MOVE, this._onHorizontalScroll, false, this);
};


/**
 *  Handle vertical scroll event
 *  @param {DvtScrollbarEvent} event scrollbar event
 *  @private
 */
DvtScrollableContainer.prototype._onVerticalScroll = function(event) {
  var delta = event.getMovedDelta();
  if (delta != 0) {
    this._translateY -= delta * this._vsbScale;
    this._contentPane.setTranslateY(this._translateY);

    //Bug 17329022 - HTML5: HORIZONTAL SCROLL TRIGGERS LOADING OF ADDT'L SEARCH RESULTS
    var x = null;
    var y = this._vsbLength * this._vsbScale + this._translateY;
    this._fireScrollEvent(x, y);
  }
};


/**
 *  Handle horizontal scroll event
 *  @param {DvtScrollbarEvent} event scrollbar event
 *  @private
 */
DvtScrollableContainer.prototype._onHorizontalScroll = function(event) {
  var delta = event.getMovedDelta();
  if (delta != 0) {
    this._translateX -= delta * this._hsbScale;
    this._contentPane.setTranslateX(this._translateX);
    var x = this._hsbLength * this._hsbScale + this._translateX;
    //Bug 17329022 - HTML5: HORIZONTAL SCROLL TRIGGERS LOADING OF ADDT'L SEARCH RESULTS
    var y = null;
    this._fireScrollEvent(x, y);
  }
};

DvtScrollableContainer.prototype._fireScrollEvent = function(x, y) {
  this.FireListener(new dvt.ScrollEvent(x, y));
};

// Bug 16815388 - HTML5 HV: TWO SCROLLBARS WERE DISPLAYED AFTER SELECTING A SEARCH RESULT
// Need to call getDimensions on each child of _contentPane in case
// it has the method getDimensions overidden
/**
 * @private
 * Gets dimensions for the content pane
 * @param {boolean} bWithStroke true for dimensions with stroke
 * @param {dvt.Displayable} targetCoordinateSpace the displayable defining the target coordinate space
 * @return {dvt.Rectangle} the bounds of the displayable relative to the target coordinate space.
 */
DvtScrollableContainer.prototype._getDimensionsForContentPane = function(bWithStroke, targetCoordinateSpace) {

  var dims = null;
  var cdims;

  for (var i = 0; i < this.getNumChildren(); i++) {
    var child = this._contentPane.getChildAt(i);

    if (child) {
      cdims = bWithStroke ? child.getDimensionsWithStroke(this._contentPane) : child.getDimensions(this._contentPane);
      if (cdims) {
        if (dims == null)
          dims = new dvt.Rectangle(cdims.x, cdims.y, cdims.w, cdims.h);
        else
          dims = dims.getUnion(cdims);
      }
    }
  }

  return targetCoordinateSpace && dims ? this.ConvertCoordSpaceRect(dims, targetCoordinateSpace) : dims;
};

/**
 * Adds scroll event listeners to the child object that has scroll event listener handler
 * @param {dvt.Displayable} obj a child object that handles scroll event
 * @private
 */
DvtScrollableContainer.prototype._addScrollEventListener = function(obj) {
  if (obj && obj.getScrollEventCallback) {
    this.addEvtListener(dvt.ScrollEvent.SCROLL_EVENT, obj.getScrollEventCallback(), false, obj);
  }
};

/**
 * Removes scroll event listeners from the child object that has scroll event listener handler
 * @param {dvt.Displayable} obj a child object that handles scroll event
 * @private
 */
DvtScrollableContainer.prototype._removeScrollEventListener = function(obj) {
  if (obj && obj.getScrollEventCallback) {
    this.removeEvtListener(dvt.ScrollEvent.SCROLL_EVENT, obj.getScrollEventCallback(), false, obj);
  }
};
// Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
/**
 * @constructor
 */
dvt.ItemList = function(context, items, minWidth, bDropShadow, backgroundStyleCallback, backgroundStyleCallbackObj, callback, calbackObj) {
  this.Init(context, items, minWidth, bDropShadow, backgroundStyleCallback, backgroundStyleCallbackObj, callback, calbackObj);
};

dvt.Obj.createSubclass(dvt.ItemList, dvt.Container, 'dvt.ItemList');

//dvt.ItemList.ITEM_LIST_EVENT = "dvtItemList";


/**
 * Interaction model: single-click.
 * The focus item changes as the mouse rolls over the items in the
 * list.  Single-clicking on an item selects it.
 */
dvt.ItemList.INT_MODEL_SINGLE_CLICK = 0;


/**
 * Interaction model: double-click.
 * Single-clicking on an item focuses it.  Double-clicking on
 * an item selects it.
 */
dvt.ItemList.INT_MODEL_DOUBLE_CLICK = 1;

dvt.ItemList._PADDING = 1;
dvt.ItemList._ITEM_FOCUS_COLOR = '#c1e6ff';//'#d1dde7';
dvt.ItemList._ALTA_PADDING = 10;//Alta padding


/**
 * @protected
 * Helper method called by constructor
 *
 * @param {dvt.Context} context Context object
 * @param {array} items An array of items to show in this popup menu
 * @param {number} minWidth Minimum width for the popup menu
 * @param {boolean} bDropShadow True to show a drop shadow, false otherwise
 * @param {function} backgroundStyleCallback Callback function that takes a Graphics object
 *        as its only argument and returns void, which sets the line and fill style
 *        for the background of this PopupMenu
 * @param {object} backgroundStyleCallbackObj  Object instance on which the backgroundStyleCallback function is defined
 * @param {function} callback Callback function that handles DvtItemListEvents
 * @param {object} callbackObj Object instance on which the callback function is defined
 */
dvt.ItemList.prototype.Init = function(context, items, minWidth, bDropShadow, backgroundStyleCallback, backgroundStyleCallbackObj, callback, callbackObj) {
  dvt.ItemList.superclass.Init.call(this, context);

  this._callback = callback;
  this._callbackObj = callbackObj;

  this._items = items;
  this._minWidth = (minWidth ? minWidth : 0);
  this._bDropShadow = (bDropShadow ? true : false);
  this._backgroundStyleCallback = backgroundStyleCallback;
  this._backgroundStyleCallbackObj = backgroundStyleCallbackObj;

  this._focusIndex = -1;
  this._hoverIndex = -1;
  this._interactionModel = dvt.ItemList.INT_MODEL_SINGLE_CLICK;

  this._focusColor = dvt.ItemList._ITEM_FOCUS_COLOR;
  this._hoverColor = null;
};

dvt.ItemList.prototype.renderComponent = function() {
  this.RenderSelf();
};

dvt.ItemList.prototype.setFocusColor = function(c) {
  this._focusColor = c;
};

dvt.ItemList.prototype.setHoverColor = function(c) {
  this._hoverColor = c;
};


/**
 * Set Alta Skin
 * @public
 * @param {boolean} bAltaSkin  Alta skin flag
 */
dvt.ItemList.prototype.setAltaSkin = function(bAltaSkin) {
  this._bAltaSkin = bAltaSkin;
};


/**
 * Check if the skin is Alta Skin
 * @protected
 * @return boolean  true if the skin is Alta
 */
dvt.ItemList.prototype.IsSkinAlta = function() {
  if (this._bAltaSkin)
    return true;

  return false;
};


/**
 * Set the interaction model for this PopupMenu based on
 * how results are chosen: single-click or double-click.
 * Constants are defined in this class and begin with INT_MODEL_.
 * The default value is INT_MODEL_SINGLE_CLICK.
 *
 * @param i type of interaction model
 */
dvt.ItemList.prototype.setInteractionModel = function(i) {
  this._interactionModel = i;
};

dvt.ItemList.prototype.FireEvent = function(event, source) {
  if (this._callback) {
    this._callback.call(this._callbackObj, event, source);
  }
};


/**
 * Fire an item list event.
 */
dvt.ItemList.prototype.FireItemListEvent = function(type, index) {
  var event = new DvtItemListEvent(type, index);
  this.FireEvent(event);
};


/**
 * Handle a mouse click on an item in the dropdown.
 *
 * @param event MouseEvent
 */
dvt.ItemList.prototype.HandleClick = function(event) {
  var s = event.target;
  if (s instanceof dvt.ItemListSprite) {
    if (this._interactionModel == dvt.ItemList.INT_MODEL_SINGLE_CLICK) {
      this.FireItemListEvent(DvtItemListEvent.TYPE_SELECTION, s.getIndex());
    }
    else {
      this.setFocusIndex(s.getIndex());
    }
  }
};


/**
 * Handle a mouse click on an item in the dropdown.
 *
 * @param event MouseEvent
 */
dvt.ItemList.prototype.HandleDblClick = function(event) {
  var s = event.target;
  if (s instanceof dvt.ItemListSprite) {
    if (this._interactionModel == dvt.ItemList.INT_MODEL_DOUBLE_CLICK) {
      this.setFocusIndex(s.getIndex());
      this.FireItemListEvent(DvtItemListEvent.TYPE_SELECTION, s.getIndex());
    }
  }
};


/**
 * Check whether the double click is enabled for the object
 * @return {boolean} true if double click is enabled for the object
 */
dvt.ItemList.prototype.isDoubleClickable = function() {
  if (this._interactionModel == dvt.ItemList.INT_MODEL_DOUBLE_CLICK) {
    return true;
  }
  return false;
};


/**
 * Handle a mouse rollover on an item in the dropdown.
 *
 * @param event MouseEvent
 */
dvt.ItemList.prototype.HandleRollOver = function(event) {
  var s = event.target;
  if (s instanceof dvt.ItemListSprite) {
    if (this._interactionModel == dvt.ItemList.INT_MODEL_SINGLE_CLICK) {
      this.setFocusIndex(s.getIndex());
    }
    else if (this.getFocusIndex() != s.getIndex()) {
      this.SetHoverIndex(s.getIndex());
    }
  }
};


/**
 * Handle a mouse rollout on an item in the dropdown.
 *
 * @param event MouseEvent
 */
dvt.ItemList.prototype.HandleRollOut = function(event) {
  var s = event.target;
  if (s instanceof dvt.ItemListSprite) {
    if (this._interactionModel == dvt.ItemList.INT_MODEL_SINGLE_CLICK) {
      this.setFocusIndex(-1);
    }
    else if (this.getFocusIndex() != s.getIndex()) {
      this.SetHoverIndex(-1);
    }
  }
};


/**
 * Highlight the given item.
 *
 * @param {dvt.Container} s item to highlight
 * @param {boolean} bFocus true for adding focus treatment to the object
 * @param {boolean} bHover true for adding hover treatment to the object
 */
dvt.ItemList.prototype.HighlightItem = function(s, bFocus, bHover) {
  //highlight the item the mouse is over
  var sDims = s.getDimensions();
  var rect;
  if (this.IsSkinAlta()) {
    var w = sDims.w + dvt.ItemList._ALTA_PADDING - DvtScrollableContainer.DEFAULT_ALTA_SCROLLBAR_WIDTH;
    w = (w < 0 ? 0 : w);
    rect = new dvt.Rect(this.getCtx(), -dvt.ItemList._ALTA_PADDING, -dvt.ItemList._ALTA_PADDING, w, sDims.h + dvt.ItemList._ALTA_PADDING);
  } else {
    rect = new dvt.Rect(this.getCtx(), 0, 0, sDims.w - 1, sDims.h - 1);
  }
  var fill;
  if (bFocus) {
    fill = new dvt.SolidFill(this._focusColor, 1);
  }
  else if (bHover) {
    fill = new dvt.SolidFill(this._hoverColor, 1);
  }
  rect.setFill(fill);
  rect.setMouseEnabled(false);
  s.addChildAt(rect, 0);
};


/**
 * Unhighlight the given item.
 *
 * @param s item to unhighlight
 */
dvt.ItemList.prototype.UnhighlightItem = function(s) {
  //remove highlighting from the item the mouse used to be over
  s.removeChildAt(0);
  //  this.RenderItemBackground(s);
};


/**
 * Get the number of items displayed.
 *
 * @return number of items displayed
 */
dvt.ItemList.prototype.getNumItems = function() {
  if (this._items) {
    return this._items.length;
  }
  return 0;
};

dvt.ItemList.prototype.getItemAt = function(i) {
  //need to offset by 1 because the overall background rect is the first child
  return this.getChildAt(i + 1);
};

dvt.ItemList.prototype.SetHoverIndex = function(i) {
  if (!this._hoverColor) {
    return;
  }

  if (this._hoverIndex > -1 && this._hoverIndex < this.getNumItems()) {
    this.UnhighlightItem(this.getItemAt(this._hoverIndex));
  }

  this._hoverIndex = i;

  if (this._hoverIndex > -1 && this._hoverIndex < this.getNumItems()) {
    this.HighlightItem(this.getItemAt(this._hoverIndex), false, true);
  }
};


/**
 * Set the index of the highlighted item.
 *
 * @param {number} i index of the highlighted item, or -1 if no item is highlighted
 * @param {boolean} bForce  true to force set the focus index
 */
dvt.ItemList.prototype.setFocusIndex = function(i, bForce) {
  if (this._focusIndex == i && !bForce)
    return;

  if (this._focusIndex > -1 && this._focusIndex < this.getNumItems()) {
    this.UnhighlightItem(this.getItemAt(this._focusIndex));
  }

  this._focusIndex = i;

  if (this._focusIndex > -1 && this._focusIndex < this.getNumItems()) {
    if (this._hoverIndex == this._focusIndex) {
      this.SetHoverIndex(-1);
    }
    this.HighlightItem(this.getItemAt(this._focusIndex), true);
  }
};


/**
 * Get the index of the highlighted item.
 *
 * @return index of the highlighted item, or -1 if no item is highlighted
 */
dvt.ItemList.prototype.getFocusIndex = function() {
  return this._focusIndex;
};


/**
 * Get the item that currently has focus, or null if no item has focus.
 *
 * @return item that current has focus
 */
dvt.ItemList.prototype.getFocusItem = function() {
  if (this._focusIndex > -1 && this._focusIndex < this.getNumItems()) {
    return this.getChildAt(this._focusIndex);
  }

  return null;
};


/**
 * Get the item at the given index.
 *
 * @param i index of the item to get
 *
 * @return item at the given index, or null if the index is not valid
 */
dvt.ItemList.prototype.getItem = function(i) {
  if (i < this.getNumItems()) {
    return this.getChildAt(i);
  }

  return null;
};


/**
 * Get the index of the item closest to the given y coord.
 *
 * @param yy y coordinate to get item for
 *
 * @return item index
 */
dvt.ItemList.prototype.getClosestItemIndex = function(yy) {
  var numItems = this.getNumItems();
  var item = null;
  //process items in order, so if y coord is less than the y coord of
  //the item + its height, then this is the item to return the index for
  //because we already passed and dismissed the previous item
  for (var i = 0; i < numItems; i++) {
    item = this.getItem(i);
    var iDims = dvt.DisplayableUtils.getDimensionsForced(this.getCtx(), item);
    if (yy < item.getTranslateY() + iDims.h) {
      return i;
    }
  }

  //if we passed all the items, then the y coord must be below the whole list,
  //so return the last item, which will be the closest
  return numItems - 1;
};


/**
 * Render the item list.
 * @protected
 */
dvt.ItemList.prototype.RenderSelf = function() {
  //iterate over the items and add them to the item list
  var itemSprite;
  var currY = dvt.ItemList._PADDING;
  var padding = dvt.ItemList._PADDING;
  if (this.IsSkinAlta()) {
    padding = dvt.ItemList._ALTA_PADDING;
    currY = dvt.ItemList._ALTA_PADDING; //Alta Padding
  }
  for (var i = 0; i < this._items.length; i++) {
    //create a new sprite to contain each item, so that feedback can
    //be drawn individually
    itemSprite = new dvt.ItemListSprite(this.getCtx(), i, this);
    this._callbackObj.getEventManager().associate(itemSprite, itemSprite);
    itemSprite.addChild(this._items[i]);

    //move each item below the previous one
    itemSprite.setTranslateX(padding);
    itemSprite.setTranslateY(itemSprite.getTranslateY() + currY);
    var iDims = dvt.DisplayableUtils.getDimensionsForced(this.getCtx(), itemSprite);
    currY += iDims.h;
    if (this.IsSkinAlta()) {
      currY += 2 * dvt.ItemList._ALTA_PADDING; //Alta Padding
    } else {
      currY += dvt.ItemList._PADDING;
    }

    //add the item to the dropdown
    this.addChild(itemSprite);
  }

  //make all items the same width
  var dims = dvt.DisplayableUtils.getDimensionsForced(this.getCtx(), this);
  var ww = Math.max(dims.w, this._minWidth);
  for (var j = 0; j < this.getNumChildren(); j++) {
    var child = this.getChildAt(j);
    this.RenderItemBackground(child, ww);

    var dispObject = this._items[j];

    //BiDi: right-align the items in the popup
    if (dvt.Agent.isRightToLeft(this.getCtx())) {
      var iDims = dvt.DisplayableUtils.getDimensionsForced(this.getCtx(), dispObject);
      var padding = this.IsSkinAlta() ? dvt.ItemList._ALTA_PADDING : dvt.ItemList._PADDING;
      dispObject.setTranslateX(ww - iDims.w - padding);
    }
    if (this.IsSkinAlta()) {
      //Add Separator for Alta Skin
      var _separator = this.MakeSeparator(ww + dvt.ItemList._ALTA_PADDING);
      dispObject.addChild(_separator);
      var offsetX = dvt.Agent.isRightToLeft(this.getCtx()) ? dispObject.getTranslateX() + dvt.ItemList._ALTA_PADDING : dvt.ItemList._ALTA_PADDING;
      _separator.setTranslateX(-offsetX);
      _separator.setTranslateY(-dvt.ItemList._ALTA_PADDING);
    }

  }

  dims = dvt.DisplayableUtils.getDimensionsForced(this.getCtx(), this);
  var rect = new dvt.Rect(this.getCtx(), 0, 0, ww + padding, dims.h + padding);
  this.addChildAt(rect, 0);
  if (this._backgroundStyleCallback) {
    this._backgroundStyleCallback.call(this._backgroundStyleCallbackObj, rect);
  }
  else {
    var stroke = new dvt.SolidStroke('#000000', 1, 1);
    var fill = new dvt.SolidFill('#ffffff');
    rect.setStroke(stroke);
    rect.setFill(fill);
  }
};


/**
 * Render the background of an item.
 *
 * @param s Sprite item
 * @param ww optional width of item
 */
dvt.ItemList.prototype.RenderItemBackground = function(s, ww) {
  if (!ww && ww != 0) {
    var dims = dvt.DisplayableUtils.getDimensionsForced(this.getCtx(), this);
    ww = dims.w - dvt.ItemList._PADDING;
  }
  var stroke = new dvt.SolidStroke('#000000', 0, 1);
  s.setStroke(stroke);
  s.setInvisibleFill();
  s.setWidth(ww - dvt.ItemList._PADDING);
  var sDims = dvt.DisplayableUtils.getDimensionsForced(this.getCtx(), s);
  s.setHeight(sDims.h);
};


/**
 * Make seperator between Dvt Items
 * @protected
 * @param {number} ww  Width of the separator
 * @return {dvt.Container}  separator object
 */
dvt.ItemList.prototype.MakeSeparator = function(ww) {
  var shape = new dvt.Container(this.getCtx());
  var line = new dvt.Path(this.getCtx(), ['M', 0, 0, 'L', ww, 0]);
  line.setSolidStroke('#D9DFE3');  //AFDvtPanelBorderColor
  shape.addChild(line);

  return shape;
};



/**
 * Gets a displayable object that represents a single search item.
 * The object is a displayable for a root element for the hierarchy of af components.
 * @param {number} itemIndex the item index
 * @return {dvt.Displayable} a displayable for the af component
 */
dvt.ItemList.prototype.getItemData = function(itemIndex) {
  if (itemIndex >= 0 && itemIndex < this._items.length)
    return this._items[itemIndex];
  else
    return null;
};


/**
 * Dvt Item list sprite inner class
 * @param {dvt.Context} context  Context object
 * @param {number} index  the item index
 * @param {dvt.ItemList} list  item list object
 * @constructor
 */
dvt.ItemListSprite = function(context, index, list) {
  this.Init(context, index, list);
};

dvt.Obj.createSubclass(dvt.ItemListSprite, dvt.Rect, 'dvt.ItemListSprite');

dvt.ItemListSprite.prototype.Init = function(context, index, list) {
  dvt.ItemListSprite.superclass.Init.call(this, context, 0, 0, 1, 1);
  this._index = index;
  this._list = list;
  this.setCursor(dvt.SelectionEffectUtils.getSelectingCursor());
  this._ariaLabel = this.getTopShortDesc();
  this.addAccessibilityAttributes();
};

dvt.ItemListSprite.prototype.getIndex = function() {
  return this._index;
};

dvt.ItemListSprite.prototype.HandleClick = function(event) {
  this._list.HandleClick(event);
};

dvt.ItemListSprite.prototype.HandleDblClick = function(event) {
  this._list.HandleDblClick(event);
};

dvt.ItemListSprite.prototype.isDoubleClickable = function() {
  return this._list.isDoubleClickable();
};

dvt.ItemListSprite.prototype.HandleRollOver = function(event) {
  this._list.HandleRollOver(event);
};

dvt.ItemListSprite.prototype.HandleRollOut = function(event) {
  this._list.HandleRollOut(event);
};

/**
 * Get the tooltip for the item list sprite
 * @return {string} tooltip or null if tooltip does not exist
 */
dvt.ItemListSprite.prototype.getTooltip = function() {
  return this.getTopShortDesc();
};

/**
 * Gets short description for the closest to the top af component that has short description
 * @return {string} short description or null if it does not exist
 */
dvt.ItemListSprite.prototype.getTopShortDesc = function() {
  var item = this._list.getItemData(this._index);
  var afComponent = dvt.AfComponentFactory.getAfComponent(item);

  //find the closest top short desc - breadth first search
  var searchQueue = [];
  var idx = 0;
  if (afComponent)
    searchQueue.push(afComponent);
  while (idx < searchQueue.length) {
    var node = searchQueue[idx++];
    var shortDesc = node.getShortDesc();
    if (shortDesc)
      return shortDesc;
    for (var i = 0; i < node.getNumChildren(); i++) {
      searchQueue.push(node.getChildAt(i));
    }
  }
  return null;
};


/**
 * @override
 */
dvt.ItemListSprite.prototype.getAriaLabel = function() {
  return this._ariaLabel;
};


/**
 * Adds accessibility attribute to the search item object
 */
dvt.ItemListSprite.prototype.addAccessibilityAttributes = function() {
  // WAI-ARIA
  this.setAriaRole('group');
  if (!dvt.Agent.deferAriaCreation()) {
    var desc = this.getAriaLabel();
    if (desc) {
      this.setAriaProperty('label', desc);
    }
  }
};
// end inner class
/////////////////////////////////////////////////
// Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
/**
 * A Train event.
 * @param {int} index The currently selected index
 * @class
 * @constructor
 */
var DvtItemListEvent = function(type, index) {
  this.Init(type);
  this._index = index;
};

dvt.Obj.createSubclass(DvtItemListEvent, dvt.BaseComponentEvent, 'DvtItemListEvent');

DvtItemListEvent.TYPE_FOCUS = 'dvtItemListFocus';
DvtItemListEvent.TYPE_SELECTION = 'dvtItemListSelection';


/**
 * Returns the index of the selected item
 * @return {index} The selected item
 */
DvtItemListEvent.prototype.getIndex = function() {
  return this._index;
};
/**
 * @constructor
 */
dvt.SearchEvent = function(type) {
  this.Init(type);
};

dvt.Obj.createSubclass(dvt.SearchEvent, dvt.BaseComponentEvent, 'dvt.SearchEvent');

dvt.SearchEvent.SEARCH_EVENT = 'dvt.SearchEvent';
dvt.SearchEvent.SEARCH_EXPAND_EVENT = 'dvtSearchExpandEvent';
dvt.SearchEvent.SEARCH_COLLAPSE_EVENT = 'dvtSearchCollapseEvent';

dvt.SearchEvent.SEARCH_RESULTS_CLEAR_EVENT = 'dvtSearchResultsClearEvent';
dvt.SearchEvent.SEARCH_RESULTS_FETCH_EVENT = 'dvtSearchResultsFetchEvent';
dvt.SearchEvent.SEARCH_RESULTS_SCROLL_EVENT = 'dvtSearchResultsScrollEvent';
dvt.SearchEvent.SEARCH_RESULTS_SELECTED_EVENT = 'dvtSearchResultSelectedEvent';

dvt.SearchEvent.PARAM_CLIENT_ROW_KEY = 'clientRowKey';
dvt.SearchEvent.PARAM_DIRECTION = 'direction';
dvt.SearchEvent.PARAM_FIRST_VIS_CLIENT_ROW_KEY = 'firstVisClientRowKey';
dvt.SearchEvent.PARAM_FIRST_VIS_INDEX = 'firstVisIndex';
dvt.SearchEvent.PARAM_INDEX = 'index';
dvt.SearchEvent.PARAM_LAST_VIS_CLIENT_ROW_KEY = 'lastVisClientRowKey';
dvt.SearchEvent.PARAM_LAST_VIS_INDEX = 'lastVisIndex';
dvt.SearchEvent.PARAM_SELECTED_CLIENT_ROW_KEY = 'selectedClientRowKey';
dvt.SearchEvent.PARAM_SELECTED_INDEX = 'selectedIndex';
dvt.SearchEvent.PARAM_TEXT = 'text';

dvt.SearchEvent.DIR_NEXT = 0;
// Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
/**
 * @constructor
 */
dvt.SearchPanel = function(context, callback, callbackObj) {
  this.Init(context, callback, callbackObj);
};

dvt.Obj.createSubclass(dvt.SearchPanel, dvt.Container, 'dvt.SearchPanel');
/**
 * @const
 * @private
 */
dvt.SearchPanel._BACKGROUND_ALPHA = .2;
/**
 * @const
 * @private
 */
dvt.SearchPanel._BACKGROUND_ALPHA_DE_EMPHASIZED = .2;
/**
 * @const
 * @private
 */
dvt.SearchPanel._BACKGROUND_ALPHA_ROLLOVER = .6;
/**
 * @const
 * @private
 */
dvt.SearchPanel._BACKGROUND_COLOR = '#ffffff';
/**
 * @const
 * @private
 */
dvt.SearchPanel._BORDER_ALPHA = 1;
/**
 * @const
 * @private
 */
dvt.SearchPanel._BORDER_ALPHA_DE_EMPHASIZED = .3;
/**
 * @const
 * @private
 */
dvt.SearchPanel._BORDER_COLOR = '#ffffff';
/**
 * @const
 * @private
 */
dvt.SearchPanel._BORDER_THICKNESS = 1;//.25;
/**
 * @const
 * @private
 */
dvt.SearchPanel._BORDER_RADIUS = 6;
/**
 * @const
 * @private
 */
dvt.SearchPanel._GO_BUTTON_WIDTH = 18;
/**
 * @const
 * @private
 */
dvt.SearchPanel._GO_BUTTON_HEIGHT = 17;
/**
 * @const
 */
dvt.SearchPanel.BUTTONWIDTH = 10;
/**
 * @const
 */
dvt.SearchPanel.BUTTONHEIGHT = 26;


/**
 * @const
 * @private
 */
dvt.SearchPanel._ALTA_CONTENT_PADDING = 10;
/**
 * @const
 * @private
 */
dvt.SearchPanel._ALTA_BUTTON_RADIUS = 2;
/**
 * @const
 * @private
 */
dvt.SearchPanel._ROUND_RECT_ELLIPSE = 4;//8;
/**
 * @const
 * @private
 */
dvt.SearchPanel._TEXT_BACKGROUND_ALPHA = .9;//.5;
/**
 * @const
 * @private
 */
dvt.SearchPanel._TEXT_BACKGROUND_ALPHA_ROLLOVER = .9;
/**
 * @const
 * @private
 */
dvt.SearchPanel._TEXT_BACKGROUND_COLOR = '#ffffff';
/**
 * @const
 * @private
 */
dvt.SearchPanel._TEXT_COLOR = '#2d3039';//"#404259";

//XML
/**
 * @const
 */
dvt.SearchPanel.ELEM_SEARCH_ADVANCED_TEMPLATE = 'saTm';
/**
 * @const
 */
dvt.SearchPanel.ELEM_SEARCH_RESULTS_DATA = 'srd';
/**
 * @const
 */
dvt.SearchPanel.ELEM_SEARCH_RESULTS_TEMPLATE = 'srTm';
/**
 * @const
 * @private
 */
dvt.SearchPanel._ELEM_EL = 'el';
/**
 * @const
 * @private
 */
dvt.SearchPanel._ELEM_F = 'f';
/**
 * @const
 * @private
 */
dvt.SearchPanel._ELEM_NODE = 'n';
/**
 * @const
 * @private
 */
dvt.SearchPanel._ELEM_NODE_DATA = 'nd';
/**
 * @const
 * @private
 */
dvt.SearchPanel._ATTR_AF_NAME = 'name';
/**
 * @const
 * @private
 */
dvt.SearchPanel._ATTR_EMPTY_TEXT = 'emptyText';
/**
 * @const
 * @private
 */
dvt.SearchPanel._ATTR_HEIGHT = 'height';
/**
 * @const
 * @private
 */
dvt.SearchPanel._ATTR_ID = 'id';
/**
 * @const
 * @private
 */
dvt.SearchPanel._ATTR_INITIAL_BEHAVIOR = 'initialBehavior';
/**
 * @const
 * @private
 */
dvt.SearchPanel._ATTR_SEARCH_RESULTS_FIRST_VISIBLE = 'searchResultsFirstVisible';
/**
 * @const
 * @private
 */
dvt.SearchPanel._ATTR_SEARCH_RESULTS_LAST_VISIBLE = 'searchResultsLastVisible';
/**
 * @const
 * @private
 */
dvt.SearchPanel._ATTR_SEARCH_RESULTS_SELECTED = 'searchResultsSelected';
/**
 * @const
 * @private
 */
dvt.SearchPanel._ATTR_SEARCH_TEXT = 'searchText';
/**
 * @const
 * @private
 */
dvt.SearchPanel._ATTR_TOTAL = 'total';
/**
 * @const
 * @private
 */
dvt.SearchPanel._ATTR_WIDTH = 'width';


/**
 * State: collapsed.
 */
dvt.SearchPanel.STATE_COLLAPSED = 0;


/**
 * State: expanded.
 * @const
 */
dvt.SearchPanel.STATE_EXPANDED = 1;


/**
 * State: embedded.
 * @const
 */
dvt.SearchPanel.STATE_EMBEDDED = 2;
/**
 * @const
 * @private
 */
dvt.SearchPanel._EXPAND_BUTTON_WIDTH = 10;
/**
 * @const
 * @private
 */
dvt.SearchPanel._TEXT_FIELD_HEIGHT = 20;
/**
 * @const
 */
dvt.SearchPanel.HEIGHT = dvt.ControlPanelLAFUtils.VIEW_PANEL_HALF_HEIGHT;


/**
 * @protected
 * Helper method called by constructor
 *
 * @param {dvt.Context} context Context object
 * @param {function} callback The function that should be called to dispatch events
 * @param {object} callbackObj Object instance on which the callback function is defined
 */
dvt.SearchPanel.prototype.Init = function(context, callback, callbackObj) {
  dvt.SearchPanel.superclass.Init.call(this, context);

  this._eventManager = new DvtSearchPanelEventManager(context, callback, callbackObj);
  this._eventManager.addListeners(this);
  this._eventManager.addRolloverType(dvt.SearchPanel);
  this._eventManager.addRolloverType(dvt.ItemListSprite);
  this._eventManager.associate(this, this);
  this._callback = callback;
  this._callbackObj = callbackObj;
  //Don't process search panel keyboard event through DvtSearchPanelEventManager
  //the search panel keyboard events are handled throw dvt.AdfDiagramEventManager
  //TO DO: Bug 18792535 Need to replace the keyboard navigation workaround
  //this._eventManager.setKeyboardHandler(new dvt.KeyboardHandler(this._eventManager));

  this._textField = null;
  this._goButton = null;
  this._frame = null;
  this._background = null;
  this._textFieldContainer = null;
  this._defaultText = 'Search';
  this._imageURIs = null;
  this._resultsTemplate = null;
  this._advancedTemplate = null;
  this._advancedComponent = null;
  this._text = this._defaultText;
  this._expandedSprite = null;
  this._collapsedSprite = null;

  this._searchResultsTotal = -1;
  this._searchText = null;
  this._searchResultsFirstVisible = null;
  this._searchResultsLastVisible = null;
  this._searchResultsSelected = null;
  this._arSearchResultsNodeData = null;
  this._bShowSearchResults = false;
  this._resultsMaxWidth = -1;
  this._resultsMaxHeight = -1;
  this._resultsTitleResource = 'Results';

  this._bTextFieldUsed = false;
  this._searchResultsPanel = null;
  this._searchResultsEmptyText = null;
  this._initialBehavior = dvt.PanZoomComponent.CONTROL_PANEL_BEHAVIOR_HIDDEN;

  this._collapseButton = null;
  this._expandButton = null;
  this._origWidth = 0;
  this._origHeight = 0;
  //temporary rect used to define size of search panel with results while results are animating in
  this._placeholderRect = null;

  this._state = dvt.SearchPanel.STATE_EXPANDED;
  this._bFocus = false;
  this._bStageFocusChangeListenersAdded = false;
  this._textFieldProxyObj = null;

  this._goTooltip = 'Search';
  this._expandTooltip = 'Show Search (Ctrl+F)';
  this._collapseTooltip = 'Hide Search (Esc)';
  this._resultsCloseTooltip = 'Close';

  this._skinName = null;

  this._textFieldHeight = dvt.SearchPanel._TEXT_FIELD_HEIGHT;
  this._textBgColor = dvt.SearchPanel._TEXT_BACKGROUND_COLOR;
  this._textBgAlpha = dvt.SearchPanel._TEXT_BACKGROUND_ALPHA;
  this._textBgAlphaRollover = dvt.SearchPanel._TEXT_BACKGROUND_ALPHA_ROLLOVER;
  this._textCornerRadius = dvt.SearchPanel._ROUND_RECT_ELLIPSE;
  this._textBorderColor = null;
  this._goButtonWidth = dvt.SearchPanel._GO_BUTTON_WIDTH;
  this._goButtonHeight = dvt.SearchPanel._GO_BUTTON_HEIGHT;

  this._styleMap = null;
  if (callbackObj)
    this._styleMap = callbackObj.getSubcomponentStyles();

  this._bgAlpha = dvt.StyleUtils.getStyle(this._styleMap, dvt.ControlPanel.BG_ALPHA, dvt.SearchPanel._BACKGROUND_ALPHA);
  this._borderColor = dvt.StyleUtils.getStyle(this._styleMap, dvt.CSSStyle.BORDER_COLOR, dvt.SearchPanel._BORDER_COLOR);
  this._borderRadius = parseInt(dvt.StyleUtils.getStyle(this._styleMap, dvt.CSSStyle.BORDER_RADIUS, dvt.SearchPanel._BORDER_RADIUS));
  this._bgColor = dvt.StyleUtils.getStyle(this._styleMap, dvt.CSSStyle.BACKGROUND_COLOR, dvt.SearchPanel._BACKGROUND_COLOR);

  //TO DO:
  //  this.tabEnabled = false;
  //  this.tabChildren = false;
  //  this.focusRect = false;
};

dvt.SearchPanel.prototype.getStyleMap = function() {
  return this._styleMap;
};


/**
 * Renders the search panel
 * @param {string} defaultText Default text for the search field
 * @param {Object} imageURIs The map containing image URIs for the component
 */
dvt.SearchPanel.prototype.renderComponent = function(defaultText, imageURIs) {
  //remove all old children before rendering
  var numChildren = this.getNumChildren();
  for (var i = numChildren - 1; i >= 0; i--) {
    this.removeChild(this.getChildAt(i));
  }
  if (this._searchResultsPanel && this._searchResultsPanel.destroy) {
    this._searchResultsPanel.destroy();
  }

  if (defaultText)
    this._defaultText = defaultText;

  var bClearTextForAlta = false;
  // Bug 17184319 - search text field with focus does not resize correctly on window resize event
  if (this._textField && this._textField.getText() != null && this._textField.getText() != '') {
    this._text = this._textField.getText();
    this._textField.destroy();
  } else {
    this._text = this._defaultText;
    //Don't show any default text for Alta skin
    //text is initialized with default value (_defaultText) to compute the bounds of the text field
    if (this.IsSkinAlta())
      bClearTextForAlta = true;
  }

  //reset all members for visual elements before rendering
  this._searchResultsPanel = null;
  this._collapseButton = null;
  this._expandButton = null;
  this._expandedSprite = null;
  this._collapsedSprite = null;
  this._textField = null;
  this._goButton = null;
  this._frame = null;
  this._background = null;
  this._textFieldContainer = null;
  this._advancedComponent = null;

  this._imageURIs = imageURIs;

  //this.addEvtListener(dvt.MouseEvent.MOUSEOVER, this.HandleRollOver, false, this);
  //this.addEvtListener(dvt.MouseEvent.MOUSEOUT, this.HandleRollOut, false, this);
  //this.addEvtListener(dvt.MouseEvent.MOUSEDOWN, dvt.EventManager.consumeEvent, false, this);
  //this.addEvtListener(dvt.MouseEvent.CLICK, dvt.EventManager.consumeEvent, false, this);
  this.addEvtListener(dvt.MouseEvent.MOUSEWHEEL, dvt.EventManager.consumeEvent, false);

  //TO DO:
  //this.addEvtListener(dvt.KeyboardEvent.KEYDOWN, this.HandleKeyDownSelf, false, this);

  if (!this._origWidth && !this._origHeight) {
    //render expanded to save full size of panel
    this.addChild(this.RenderSelfExpanded());

    //save the background size instead of the panel dimensions, because
    //the expand/collapse icon has hidden padding that artificially distorts
    //the dimensions
    //var dims = dvt.DisplayableUtils.getDimensionsForced(this.getCtx(), this);
    //this._origWidth = dims.w;
    //this._origHeight = dims.h;
    this._origWidth = this._background.getWidth();
    this._origHeight = this._background.getHeight();

    //collapse panel if necessary
    if (this._state == dvt.SearchPanel.STATE_COLLAPSED) {
      this._doCollapse(true);
    }
  }
  else {
    if (this._state == dvt.SearchPanel.STATE_COLLAPSED) {
      this.addChild(this.RenderSelfCollapsed());
    }
    else {
      this.addChild(this.RenderSelfExpanded());
    }
  }
  //Don't display Default Text for Alta Skin
  if (bClearTextForAlta) {
    this._text = '';
    this._textField.setText(this._text);
  }
  if (this._state != dvt.SearchPanel.STATE_COLLAPSED && this._bShowSearchResults) {
    this.DisplaySearchResults();
  }
};


/**
 * Set the text to show in the search field.
 *
 * @param s text to show
 */
dvt.SearchPanel.prototype.setText = function(s) {
  this._text = s;
  if (this._textField) {
    this._textField.setText(this._text);
  }
  //mark the field as used so that the text is not cleared like default
  //text when the field is clicked
  this._bTextFieldUsed = true;
};


/**
 * Set the search panel behavior: initExpanded, initCollapsed, or hidden.
 * The default is hidden.
 *
 * @param behavior search panel behavior
 */
dvt.SearchPanel.prototype.setInitialBehavior = function(behavior) {
  this._initialBehavior = behavior;

  if (!this._origWidth && !this._origHeight && behavior == dvt.PanZoomComponent.CONTROL_PANEL_BEHAVIOR_INIT_COLLAPSED) {
    this._state = dvt.SearchPanel.STATE_COLLAPSED;
  }
};


/**
 * Get the search panel behavior.
 *
 * @return search panel behavior: initExpanded, initCollapsed, or hidden
 */
dvt.SearchPanel.prototype.getInitialBehavior = function() {
  return this._initialBehavior;
};


/**
 * Set the text to show when there are no search results returned.
 *
 * @param s text to show when there are no search results returned
 */
dvt.SearchPanel.prototype.setSearchResultsEmptyText = function(s) {
  this._searchResultsEmptyText = s;
};


/**
 * Get the text to show when there are no search results returned.
 *
 * @return text to show when there are no search results returned
 */
dvt.SearchPanel.prototype.getSearchResultsEmptyText = function() {
  //TO DO:
  return this._searchResultsEmptyText;
  //  return this._searchResultsEmptyText ?
  //         this._searchResultsEmptyText :
  //         this.getResources().getSearchResultsEmptyText();
};

/**
 * Get Search text
 * @return {String} search text
 */
dvt.SearchPanel.prototype.getSearchText = function() {
  return this._searchText;
};

/**
 * Get search results total
 * @return {Number} search results total
 */
dvt.SearchPanel.prototype.getSearchResultsTotal = function() {
  return this._searchResultsTotal;
};

/**
 * Returns whether the search component is disclosed
 * @return {boolean} Whether the search component is disclosed
 */
dvt.SearchPanel.prototype.isDisclosed = function() {
  return this.getDiagram().isSearchPanelDisclosed();
};

/**
 * Set the tooltip to show for the Go button.
 *
 * @param s tooltip for Go button
 */
dvt.SearchPanel.prototype.setGoTooltip = function(s) {
  this._goTooltip = s;
};


/**
 * Set the tooltip to show for the Expand button.
 *
 * @param s tooltip for Expand button
 */
dvt.SearchPanel.prototype.setExpandTooltip = function(s) {
  this._expandTooltip = s;
};


/**
 * Set the tooltip to show for the Collapse button.
 *
 * @param s tooltip for Collapse button
 */
dvt.SearchPanel.prototype.setCollapseTooltip = function(s) {
  this._collapseTooltip = s;
};


/**
 * Set the tooltip to show for the results list Close button.
 *
 * @param s tooltip for results list Close button
 */
dvt.SearchPanel.prototype.setResultsCloseTooltip = function(s) {
  this._resultsCloseTooltip = s;
};

dvt.SearchPanel.prototype.setResultsMaxWidth = function(ww) {
  this._resultsMaxWidth = ww;
};

dvt.SearchPanel.prototype.setResultsMaxHeight = function(hh) {
  this._resultsMaxHeight = hh;
};

dvt.SearchPanel.prototype.setResultsTitleResource = function(s) {
  this._resultsTitleResource = s;
};


/**
 * Set the state of this control panel.
 *
 * @param state STATE_EXPANDED or STATE_COLLAPSED
 */
dvt.SearchPanel.prototype.setState = function(state) {
  if (this._state != state) {
    if (this._origWidth && this._origHeight) {
      this.toggleExpandCollapse();
    }
    this._state = state;
  }
};


/**
 * Get the state of this control panel.
 *
 * @return STATE_EXPANDED or STATE_COLLAPSED
 */
dvt.SearchPanel.prototype.getState = function() {
  return this._state;
};

dvt.SearchPanel.prototype.IsEmbedded = function() {
  return (this._state == dvt.SearchPanel.STATE_EMBEDDED);
};


/**
 * Toggle the state of the control panel between expanded and collapsed.
 */
dvt.SearchPanel.prototype.toggleExpandCollapse = function(onEnd) {
  if (this._state == dvt.SearchPanel.STATE_EXPANDED) {
    this._doCollapse();
  }
  else if (this._state == dvt.SearchPanel.STATE_COLLAPSED) {
    this._doExpand(onEnd);
  }
};


/**
 * Get the visual width of the expanded panel.
 *
 * @return visual width of expanded panel
 */
dvt.SearchPanel.prototype.getWidth = function() {
  return this._origWidth;
};


/**
 * Get the visual height of the expanded panel.
 *
 * @return visual height of expanded panel
 */
dvt.SearchPanel.prototype.getHeight = function() {
  return this._origHeight;
};


/**
 * Set Button container style
 * @param {dvt.CSSStyle} buttonStyle  Button container Style
 */
dvt.SearchPanel.prototype.setButtonStyle = function(buttonStyle) {
  this._buttonStyle = buttonStyle;
};


/**
 * Get Button container style
 * @return {dvt.CSSStyle} Button container Style
 */
dvt.SearchPanel.prototype.getButtonStyle = function() {
  return this._buttonStyle;
};


/**
 * Set Button label style
 * @param {dvt.CSSStyle} labelStyle  Button label style
 */
dvt.SearchPanel.prototype.setLabelStyle = function(labelStyle) {
  this._labelStyle = labelStyle;
};


/**
 * Get Button label style
 * @return {dvt.CSSStyle}  Button label style
 */
dvt.SearchPanel.prototype.getLabelStyle = function() {
  return this._labelStyle;
};

/**
 * Get go button
 * @return {dvt.Button} go button
 */
dvt.SearchPanel.prototype.getGoButton = function() {
  return this._goButton;
};

/**
 * Get text field
 * @return {dvt.Text} text field
 */
dvt.SearchPanel.prototype.getTextField = function() {
  return this._textField;
};

/**
 * Get search results panel
 * @return {DvtSearchResultsPanel} results panel
 */
dvt.SearchPanel.prototype.getSearchResultsPanel = function() {
  return this._searchResultsPanel;
};

/**
 * Get AF Component root
 * @return {dvt.AfComponent} af component root
 */
dvt.SearchPanel.prototype.getAfComponentRoot = function() {
  return dvt.AfComponentFactory.getAfComponent(this._advancedComponent);
};

/**
 * Get AF component from the search panel for the passed component id
 * @param {String} componentId  component id
 * @return {dvt.AfComponent} Af Component
 */
dvt.SearchPanel.prototype.getAfComponent = function(componentId) {
  var rootComp = this.getAfComponentRoot();
  return dvt.AfComponentFactory.findAfComponent(rootComp, componentId);
};


/**
 * Destroys search panel - removes event listeners,
 * removes search results panel from tab stops array and destroys search results panel
 */
dvt.SearchPanel.prototype.destroy = function() {
  this.removeEvtListener(dvt.MouseEvent.MOUSEWHEEL, dvt.EventManager.consumeEvent, false);
  this.removeEvtListener(dvt.KeyboardEvent.KEYDOWN, this.HandleKeyDownSelf, false, this);

  if (this._searchResultsPanel && this._searchResultsPanel.destroy) {
    this._searchResultsPanel.destroy();
  }
  this._removeTabStop(this._searchResultsPanel);
  this._searchResultsPanel = null;
};


/**
 * De-emphasize the search panel, for example while dragging other content
 * around in the window.
 */
dvt.SearchPanel.prototype.deEmphasizeStart = function() {
  //disable interaction
  this.setMouseEnabled(false);
  if (this._background) {
    this._background.setAlpha(dvt.SearchPanel._BACKGROUND_ALPHA_DE_EMPHASIZED);
  }
  if (this._collapseButton) {
    this._collapseButtonAlpha = this._collapseButton.getAlpha();
    this._collapseButton.setAlpha(dvt.SearchPanel._BACKGROUND_ALPHA_DE_EMPHASIZED);
  }
  if (this._frame) {
    this._frame.setAlpha(dvt.SearchPanel._BORDER_ALPHA_DE_EMPHASIZED);
  }
};


/**
 * Stop de-emphasizing the search panel.
 */
dvt.SearchPanel.prototype.deEmphasizeEnd = function() {
  //enable interaction
  this.setMouseEnabled(true);

  if (this._background != null) {
    this._background.setAlpha(this._bgAlpha);
  }
  if (this._collapseButton) {
    this._collapseButton.setAlpha(this._collapseButtonAlpha);
    this._collapseButtonAlpha = null;
  }
  if (this._frame != null) {
    this._frame.setAlpha(dvt.SearchPanel._BORDER_ALPHA);
  }

  //change alphas based on whether the mouse is currently over
  //the search panel
  //TO DO:
  var bMouseOver = false;//this.hitTestPoint(stage.mouseX, stage.mouseY, true);
  if (bMouseOver) {
    this.HandleRollOver(null);
  }
  else {
    this.HandleRollOut(null);
  }
};


/**
 * Give focus to the text field.
 */
dvt.SearchPanel.prototype.focusTextField = function() {
  var currTabStop = this.getCurrentTabStop();
  if (currTabStop && currTabStop != this && currTabStop.hideKeyboardFocusEffect) {
    currTabStop.hideKeyboardFocusEffect();
  }
  this._curTabStopIdx = 0;

  //mark as focused
  this._bFocus = true;
  this.ApplyAlphasRollover();
  if (this._textField) {
    this.HandleTextFieldMouseDown();
    this._textField.selectEditText();
  }
};


/**
 * @private
 * Remove focus from the text field.
 */
dvt.SearchPanel.prototype._blurTextField = function() {
  this.HandleTextFieldFocusOut();
  this._bFocus = false;
  this._eventManager.setFocus(null);
  this.ApplyAlphasForMouse();
};

/**
 * Add focus change listeners to the stage.
 */
/*dvt.SearchPanel.prototype.AddStageFocusChangeListeners = function() {
  //TO DO:
  var stage = this.getCtx().getStage();
  if (!this._bStageFocusChangeListenersAdded && stage) {
    this._bStageFocusChangeListenersAdded = true;
    //stage.addEvtListener(FocusEvent.MOUSE_FOCUS_CHANGE, this.HandleStageFocusChange, false, this);
    //stage.addEvtListener(FocusEvent.KEY_FOCUS_CHANGE, this.HandleStageFocusChange, false, this);
    stage.addEvtListener(dvt.MouseEvent.MOUSEDOWN, this.HandleStageFocusChange, true, this);
  }
};*/

/**
 * Remove focus change listeners from the stage.
 */
/*dvt.SearchPanel.prototype.RemoveStageFocusChangeListeners = function() {
  //TO DO:
  var stage = this.getCtx().getStage();
  if (this._bStageFocusChangeListenersAdded && stage) {
    this._bStageFocusChangeListenersAdded = false;
    //stage.removeEvtListener(FocusEvent.MOUSE_FOCUS_CHANGE, this.HandleStageFocusChange, false, this);
    //stage.removeEvtListener(FocusEvent.KEY_FOCUS_CHANGE, this.HandleStageFocusChange, false, this);
    stage.removeEvtListener(dvt.MouseEvent.MOUSEDOWN, this.HandleStageFocusChange, true, this);
  }
};*/


/**
 * Handle a focus change event on the stage.
 *
 * @param event FocusEvent
 */
dvt.SearchPanel.prototype.HandleStageFocusChange = function(event) {
  if (this._bFocus) {
    //    if (event.relatedObject) {
    if (event.target) {
      //      var intObj = event.relatedObject;
      var intObj = event.target;
      //if the search panel was focused, and the new focus obj
      //is not a child of the panel, then toggle the focused flag
      //and re-apply alphas
      //      if (!this.IsChildOfSearch(intObj)) {
      if (intObj != this._textField) {
        this._blurTextField();
      }
    }
  }
};

dvt.SearchPanel.prototype.setSkinName = function(name) {
  this._skinName = name;

  if (this.IsSkinAlta()) {
    this._textFieldHeight = 26;
    this._textBgColor = '#fcfdfe';
    this._textBgAlpha = 1;
    this._textBgAlphaRollover = 1;
    this._textCornerRadius = 2;
    this._textBorderColor = '#dfe4e7';
    this._goButtonWidth = null;
    this._goButtonHeight = this._textFieldHeight;
  }
};

dvt.SearchPanel.prototype.IsSkinAlta = function() {
  return (this._skinName == dvt.CSSStyle.SKIN_ALTA);
};

/**
 * Determine if the given DisplayObject is a child of this SearchPanel.
 *
 * @param obj DisplayObject to determine parentage for
 *
 * @return true if the obj is a child of, or is this, SearchPanel,
 *         false otherwise
 */
/*dvt.SearchPanel.prototype.IsChildOfSearch = function(obj) {
  var comp = obj;
  while (comp) {
    if (comp == this) {
      return true;
    }

    comp = comp.getParent();
  }

  return false;
};*/

dvt.SearchPanel.prototype.DisplaySearchResults = function(bImmediate) {
  if (bImmediate) {
    this.DestroySearchResultsPanel();
  }
  this.CreateSearchResultsPanel();
  if (this._state != dvt.SearchPanel.STATE_COLLAPSED) {
    //if embedded in panel drawer, get the dimensions and fire resize event
    //before calling ShowSearchResultsPanel
    if (this._state == dvt.SearchPanel.STATE_EMBEDDED) {
      //make sure results are in display list before getting dimensions
      var bAdded = (this._searchResultsPanel.getParent() || false);
      if (!bAdded) {
        this.addChild(this._searchResultsPanel);
      }
      var dims = this.getDimensions();
      this.FireListener(new dvt.ResizeEvent(dims.w, dims.h, dims.x, dims.y));
      if (!bAdded) {
        this.removeChild(this._searchResultsPanel);
      }

      //add a temporary rect to define the size of the displayed search results while the
      //results are animating in, so that the panelDrawer will be sized correctly
      this.RemovePlaceholderRect();
      this._placeholderRect = new dvt.Rect(this.getCtx(), dims.x, dims.y, dims.w, dims.h);
      this._placeholderRect.setAlpha(0);
      this._placeholderRect.setMouseEnabled(false);
      this.addChildAt(this._placeholderRect, 0);
    }

    var anim = this.ShowSearchResultsPanel(bImmediate);
    if (anim) {
      if (this._placeholderRect) {
        dvt.Playable.appendOnEnd(anim, this.RemovePlaceholderRect, this);
      }
      anim.play();
    }
    else if (this._placeholderRect) {
      this.RemovePlaceholderRect();
    }
  }
};

dvt.SearchPanel.prototype.RemovePlaceholderRect = function() {
  if (this._placeholderRect) {
    var phParent = this._placeholderRect.getParent();
    if (phParent) {
      phParent.removeChild(this._placeholderRect);
    }
  }
  this._placeholderRect = null;
};

dvt.SearchPanel.prototype.StampSearchResults = function(arData) {
  var items = [];
  if (arData && arData.length > 0) {
    for (var i = 0; i < arData.length; i++) {
      var data = arData[i];
      var afContext = this.CreateAfContext();
      afContext.setELContext(data.getElAttributes());
      var item = dvt.AfComponentFactory.parseAndLayout(afContext, this._resultsTemplate, this);
      item.setMouseEnabled(false);
      items.push(item);
    }
  }
  return items;
};


/**
 * Create Search Results Panel
 * @protected
 */
dvt.SearchPanel.prototype.CreateSearchResultsPanel = function() {
  var items = this.StampSearchResults(this._arSearchResultsNodeData);
  var total = this._searchResultsTotal;
  var firstVisIndex = this._searchResultsFirstVisible;
  var lastVisIndex = this._searchResultsLastVisible;
  var selectedIndex = this._searchResultsSelected;
  var maxWidth = this._resultsMaxWidth;
  var maxHeight = this._resultsMaxHeight;
  var titleResource = this._resultsTitleResource;

  if (!maxWidth && maxWidth != 0) {
    maxWidth = -1;
  }
  if (!maxHeight && maxHeight != 0) {
    maxHeight = -1;
  }
  if (!firstVisIndex && firstVisIndex != 0) {
    firstVisIndex = -1;
  }
  if (!lastVisIndex && lastVisIndex != 0) {
    lastVisIndex = -1;
  }
  if (!selectedIndex && selectedIndex != 0) {
    selectedIndex = -1;
  }

  //minWidth is distance from start of text field to end of panel
  var minWidth = this._origWidth - this._textFieldContainer.getTranslateX();
  //BiDi: minWidth is distance from start of panel to end of text field
  if (dvt.Agent.isRightToLeft(this.getCtx())) {
    minWidth = this._textFieldContainer.getTranslateX() + this._textFieldContainer.getDimensions().w + 1;
  }

  var padY = 1;
  if (this.IsSkinAlta()) {
    padY = 7;
    minWidth += dvt.SearchPanel._ALTA_CONTENT_PADDING;
  }
  var resultsTy = this._origHeight + padY;

  this._searchResultsPanel = new DvtSearchResultsPanel(this.getCtx(), titleResource, this.getSearchResultsEmptyText(), this.HandleSearchResultsEvent, this);
  this._tabStopsNodeArray.push(this._searchResultsPanel); // add search results panel as tab stop
  this._searchResultsPanel.setSkinName(this._skinName);
  //Bug 15839275: need to decrease the maxHeight passed to results by offset of the results themselves
  this._searchResultsPanel.renderComponent(this, items, total, maxWidth, maxHeight - resultsTy, minWidth, firstVisIndex, lastVisIndex, selectedIndex, this._imageURIs);
  this._searchResultsPanel.setCloseTooltip(this._resultsCloseTooltip);
  //BiDi: always position results panel lined up with left side of search panel
  if (dvt.Agent.isRightToLeft(this.getCtx()) || this.IsSkinAlta()) {
    var padX = 10;
    if (this.IsSkinAlta())
      padX = 0;
    this._searchResultsPanel.setTranslateX(padX);
  }
  else {
    //if results width is greater than min width, then right-align with end of panel
    if (this._searchResultsPanel.getWidth() > minWidth) {
      this._searchResultsPanel.setTranslateX(this._origWidth - this._searchResultsPanel.getWidth());
    }
    //if results width is less than min width, then left-align with start of
    //text field, which is still basically right-aligned with end of panel
    //because of min width
    else {
      this._searchResultsPanel.setTranslateX(this._textFieldContainer.getTranslateX());
    }
  }
  this._searchResultsPanel.setTranslateY(resultsTy);
};

dvt.SearchPanel.prototype.ShowSearchResultsPanel = function(bImmediate) {
  this.addChild(this._searchResultsPanel);
  return this._searchResultsPanel.show(bImmediate);
};

/**
 * @protected
 * Hide search results panel
 * @param {boolean} bDestroy  true to destroy search results panel after hiding
 * @return {dvt.Animator}  animator object if animation is enabled
 */
dvt.SearchPanel.prototype.HideSearchResultsPanel = function(bDestroy) {
  var t = null;
  if (this._searchResultsPanel) {
    if (this._searchResultsPanel.getVisible()) {
      t = this._searchResultsPanel.hide();
      if (bDestroy) {
        if (t)
          t.onEnd = this.DestroySearchResultsPanel;
        else
          this.DestroySearchResultsPanel();
      }
      //fire a resize event when results are hidden
      if (this._state == dvt.SearchPanel.STATE_EMBEDDED) {
        this.FireListener(new dvt.ResizeEvent(this.getWidth(), this.getHeight(), 0, 0));
      }
    }
    else {
      if (bDestroy) {
        this.DestroySearchResultsPanel();
      }
    }
  }
  return t;
};

/**
 * @protected
 * Destroy search results panel
 */
dvt.SearchPanel.prototype.DestroySearchResultsPanel = function() {
  if (this._searchResultsPanel) {
    if (this._searchResultsPanel.getParent()) {
      this._searchResultsPanel.getParent().removeChild(this._searchResultsPanel);
    }
    this._removeTabStop(this._searchResultsPanel);
    if (this._searchResultsPanel.destroy) {
      this._searchResultsPanel.destroy();
    }
    this._searchResultsPanel = null;
  }
};

dvt.SearchPanel.prototype.HandleSearchResultsEvent = function(event) {
  if (event['type'] == dvt.SearchEvent.SEARCH_RESULTS_CLEAR_EVENT) {
    //toggle flag to not show results if search panel is collapsed and
    //expanded again
    this._bShowSearchResults = false;
    //destroy the results panel
    this.DestroySearchResultsPanel();
    this._searchResultsPanel = null;
    //clear the results data
    this.ClearResultsNodeData();

    //fire a resize event when results are cleared
    if (this._state == dvt.SearchPanel.STATE_EMBEDDED) {
      var dims = this.getDimensions();
      this.FireListener(new dvt.ResizeEvent(dims.w, dims.h, dims.x, dims.y));
    }
  }
  else if (event['type'] == dvt.SearchEvent.SEARCH_RESULTS_SELECTED_EVENT) {
    //TO DO: do we really want to hide the results panel here?
    //hide the results panel
    //this.HideSearchResultsPanel();

    var index = event.getParamValue(dvt.SearchEvent.PARAM_INDEX);
    var id = this.GetResultId(index);
    if (id) {
      event.addParam(dvt.SearchEvent.PARAM_CLIENT_ROW_KEY, id);
    }
  }
  else if (event['type'] == dvt.SearchEvent.SEARCH_RESULTS_FETCH_EVENT) {
    var index = event.getParamValue(dvt.SearchEvent.PARAM_INDEX);
    var id = this.GetResultId(index);
    if (id) {
      event.addParam(dvt.SearchEvent.PARAM_CLIENT_ROW_KEY, id);
    }
  }
  else if (event['type'] == dvt.SearchEvent.SEARCH_RESULTS_SCROLL_EVENT) {
    var index = event.getParamValue(dvt.SearchEvent.PARAM_FIRST_VIS_INDEX);
    var id = this.GetResultId(index);
    if (id) {
      event.addParam(dvt.SearchEvent.PARAM_FIRST_VIS_CLIENT_ROW_KEY, id);
    }
    // See Bug 8921603: middle tier doesn't need this now
    //index = event.getParamValue(dvt.SearchEvent.PARAM_LAST_VIS_INDEX);
    //id = this.GetResultId(index);
    //if (id) {
    //  event.addParam(dvt.SearchEvent.PARAM_LAST_VIS_CLIENT_ROW_KEY, id);
    //}
    index = event.getParamValue(dvt.SearchEvent.PARAM_SELECTED_INDEX);
    id = this.GetResultId(index);
    if (id) {
      event.addParam(dvt.SearchEvent.PARAM_SELECTED_CLIENT_ROW_KEY, id);
    }
  }
  //re-fire the event
  this.FireEvent(event);
};

dvt.SearchPanel.prototype.GetResultId = function(index) {
  if (this._arSearchResultsNodeData &&
      this._arSearchResultsNodeData.length > index)
  {
    var nodeData = this._arSearchResultsNodeData[index];
    if (nodeData) {
      return nodeData.getId();
    }
  }
  return null;
};

dvt.SearchPanel.prototype.FireEvent = function(event, source) {
  if (this._callback) {
    this._callback.call(this._callbackObj, event, source);
  }
};


/**
 * Fire a search event.
 *
 * @param text text to search for
 *
 * @return true if event was successfully dispatched, false otherwise
 */
dvt.SearchPanel.prototype.FireSearchEvent = function(text) {
  //clear current results when doing a new search
  this.ClearResultsNodeData();

  var event = new dvt.SearchEvent(dvt.SearchEvent.SEARCH_EVENT);
  event.addParam(dvt.SearchEvent.PARAM_TEXT, text);
  this.FireEvent(event);
};

dvt.SearchPanel.prototype.FireExpandEvent = function() {
  this.FireEvent(new dvt.SearchEvent(dvt.SearchEvent.SEARCH_EXPAND_EVENT));
};

dvt.SearchPanel.prototype.FireCollapseEvent = function() {
  this.FireEvent(new dvt.SearchEvent(dvt.SearchEvent.SEARCH_COLLAPSE_EVENT));
};


/**
 * Handle mouse roll over on the search panel.
 *
 * @param event MouseEvent
 */
dvt.SearchPanel.prototype.HandleRollOver = function(event) {
  //this.ApplyAlphasForMouse();
  this.ApplyAlphasRollover();
};


/**
 * Handle mouse roll out on the search panel.
 *
 * @param event MouseEvent
 */
dvt.SearchPanel.prototype.HandleRollOut = function(event) {
  //this.ApplyAlphasForMouse();
  if (!this._bFocus) {
    this.ApplyAlphasRollout();
  }
};


/**
 * Apply alpha values for mouse roll over.
 */
dvt.SearchPanel.prototype.ApplyAlphasRollover = function() {
  if (this._textFieldContainer) {
    this._textFieldContainer.setAlpha(this._textBgAlphaRollover);
  }
  if (this._background) {
    this._background.setAlpha(dvt.SearchPanel._BACKGROUND_ALPHA_ROLLOVER);
  }
};


/**
 * Apply alpha values for mouse roll out.
 */
dvt.SearchPanel.prototype.ApplyAlphasRollout = function() {

  if (this._textFieldContainer) {
    this._textFieldContainer.setAlpha(this._textBgAlpha);
  }
  if (this._background) {
    this._background.setAlpha(this._bgAlpha);
  }
};


/**
 * Apply alphas based on whether the mouse is over the control panel.
 */
dvt.SearchPanel.prototype.ApplyAlphasForMouse = function() {
  //apply alphas based on whether the mouse is over the control panel
  var bMouseOver = true;//false;
  //TO DO: figure out how to hit test the mouse point
  //  if (stage) {
  //    bMouseOver = this.HitTestPoint(stage.mouseX, stage.mouseY, true);
  //  }
  if (bMouseOver || this._bFocus) {
    this.ApplyAlphasRollover();
  }
  else {
    this.ApplyAlphasRollout();
  }
};


/**
 * Expand the search panel.
 *
 * @param onEnd function, which takes no arguments, to be executed after expanding
 */
dvt.SearchPanel.prototype._doExpand = function(onEnd) {
  //render the expanded control panel
  var s = this.RenderSelfExpanded();
  this.addChild(s);

  //below line moved to RenderSelfExpanded
  //this.CheckShowDefaultText();

  //transition from the collapsed control panel to the expanded one
  var dvtT = this.TransitionExpand(this._collapsedSprite, s);

  this._state = dvt.SearchPanel.STATE_EXPANDED;
  this.FireExpandEvent();

  //restore results after expanding panel
  if (this._searchResultsPanel) {
    var thisRef = this;
    var func = function() {
      var showResultsT = thisRef.ShowSearchResultsPanel();
      if (onEnd) {
        dvt.Playable.appendOnEnd(showResultsT, onEnd);
      }
      showResultsT.play();
    };
    dvt.Playable.appendOnEnd(dvtT, func);
  }
  else if (onEnd) {
    dvt.Playable.appendOnEnd(dvtT, onEnd);
  }

  dvtT.play();
};


/**
 * Collapse the search panel.
 */
dvt.SearchPanel.prototype._doCollapse = function(bImmediate) {
  var t = this.HideSearchResultsPanel(false);

  //save the text to show when the panel is next expanded
  if (this._textField) {
    this._text = this._textField.getText();
  }

  //render the collapsed control panel
  var s = this.RenderSelfCollapsed();
  this.addChild(s);

  this._bFocus = false;
  //this.RemoveStageFocusChangeListeners();

  //transition from the expanded control panel to the collapsed one
  var dvtT = this.TransitionCollapse(this._expandedSprite, s, bImmediate);

  this._state = dvt.SearchPanel.STATE_COLLAPSED;
  this.FireCollapseEvent();

  if (!t) {
    if (dvtT) {
      dvtT.play();
    }
  }
  else {
    if (dvtT) {
      var func = function() {
        dvtT.play();
      };
      dvt.Playable.appendOnEnd(t, func);
    }
    t.play();
  }
};


/**
 * @protected
 * Animate the transition from the collapsed control to the expanded control.
 *
 * @param {dvt.Container} oldContent collapsed control
 * @param {dvt.Container} newContent expanded control
 */
dvt.SearchPanel.prototype.TransitionExpand = function(oldContent, newContent) {
  var t = new dvt.Animator(this.getCtx(), .25);

  //fade out old, fade in new
  oldContent.setAlpha(1);
  newContent.setAlpha(0);
  t.addProp(dvt.Animator.TYPE_NUMBER, oldContent, oldContent.getAlpha, oldContent.setAlpha, 0);
  t.addProp(dvt.Animator.TYPE_NUMBER, newContent, newContent.getAlpha, newContent.setAlpha, 1);

  //grow new control from size of old
  var newDims = newContent.getDimensions();
  var oldDims = oldContent.getDimensions();
  newContent.setScaleX(oldDims.w / newDims.w);
  t.addProp(dvt.Animator.TYPE_NUMBER, newContent, newContent.getScaleX, newContent.setScaleX, 1);
  //only need to modify x coord if locale is left-to-right, because search
  //panel is on right side and expands out to left
  if (!dvt.Agent.isRightToLeft(this.getCtx())) {
    newContent.setTranslateX(oldContent.getTranslateX());
    t.addProp(dvt.Animator.TYPE_NUMBER, newContent, newContent.getTranslateX, newContent.setTranslateX, 0);
  }

  newContent.setScaleY(oldDims.h / newDims.h);
  t.addProp(dvt.Animator.TYPE_NUMBER, newContent, newContent.getScaleY, newContent.setScaleY, 1);

  //remove the old control at the end of the transition
  dvt.Playable.appendOnEnd(t, function() {oldContent.getParent().removeChild(oldContent)});

  dvt.Playable.appendOnEnd(t, this.ApplyAlphasForMouse, this);

  this._collapsedSprite = null;

  return t;
};


/**
 * @protected
 * Animate the transition from the expanded control to the collapsed control.
 *
 * @param {dvt.Container} oldContent expanded control
 * @param {dvt.Container} newContent collapsed control
 */
dvt.SearchPanel.prototype.TransitionCollapse = function(oldContent, newContent, bImmediate) {
  var t = null;
  if (!bImmediate) {
    t = new dvt.Animator(this.getCtx(), .25);
  }

  //fade out old, fade in new
  if (t) {
    oldContent.setAlpha(1);
    newContent.setAlpha(0);
    t.addProp(dvt.Animator.TYPE_NUMBER, oldContent, oldContent.getAlpha, oldContent.setAlpha, 0);
    t.addProp(dvt.Animator.TYPE_NUMBER, newContent, newContent.getAlpha, newContent.setAlpha, 1);
  }
  else {
    oldContent.alpha = 0;
    newContent.alpha = 1;
  }

  //shrink old control to size of new
  if (t) {
    var newDims = newContent.getDimensions();
    var oldDims = oldContent.getDimensions();
    t.addProp(dvt.Animator.TYPE_NUMBER, oldContent, oldContent.getScaleX, oldContent.setScaleX, (newDims.w / oldDims.w));
    t.addProp(dvt.Animator.TYPE_NUMBER, oldContent, oldContent.getScaleY, oldContent.setScaleY, (newDims.h / oldDims.h));

    //only need to modify x coord if locale is left-to-right, because search
    //panel is on right side and collapses to the right
    if (!dvt.Agent.isRightToLeft(this.getCtx())) {
      t.addProp(dvt.Animator.TYPE_NUMBER, oldContent, oldContent.getTranslateX, oldContent.setTranslateX, newContent.getTranslateX());
    }
  }

  //remove the old control at the end of the transition
  if (t) {
    var thisRef = this;
    dvt.Playable.appendOnEnd(t, function() {
      oldContent.getParent().removeChild(oldContent);
      thisRef.ApplyAlphasForMouse();
    });
  }
  else {
    oldContent.getParent().removeChild(oldContent);
    this.ApplyAlphasForMouse();
  }

  this._expandedSprite = null;

  return t;
};


/**
 * @protected
 * Render the collapsed panel.
 *
 * @return {dvt.Container} container representing collapsed state
 */
dvt.SearchPanel.prototype.RenderSelfCollapsed = function() {
  var s = new dvt.Container(this.getCtx());
  var bR2L = dvt.Agent.isRightToLeft(this.getCtx());

  //Bug 14636627: attach to the display list while rendering
  this.addChild(s);

  var hh = dvt.ControlPanelLAFUtils.getViewPanelHalfHeight();
  this._background = dvt.ControlPanelLAFUtils.createEmptyViewClosedShape(this.getCtx(), hh, this._styleMap, !bR2L);

  this._background.setAlpha(this._bgAlpha);
  s.addChild(this._background);

  this._frame = new dvt.Container(this.getCtx());
  var s2 = dvt.ControlPanelLAFUtils.createEmptyViewClosedFrame(this.getCtx(), hh, this._styleMap, !bR2L);

  this._frame.addChild(s2);
  s.addChild(this._frame);

  this._expandButton = this.CreateExpandButton();
  //BiDi: only adjust button if locale is left-to-right
  if (!bR2L) {
    this._expandButton.setTranslateX(this._expandButton.getTranslateX() + 1);
  }
  this._frame.addChild(this._expandButton);

  //only need to offset collapsed panel if locale is left-to-right
  if (!bR2L) {
    //var sDims = s.getDimensions();
    //s.setTranslateX(this._origWidth - (sDims.x + sDims.w));// - 6);
    //explicitly use the collapse button width, because the image
    //size artificially inflates the dimensions due to transparent padding
    var buttonWidth = dvt.StyleUtils.getStyle(this._styleMap, dvt.ControlPanel.CP_OPEN_CLOSE_BUTTON_WIDTH, 0);
    s.setTranslateX(this._origWidth - buttonWidth);
  }

  this._collapsedSprite = s;

  //Bug 14636627: remove from the display list after rendering
  this.removeChild(s);

  return s;
};


/**
 * Create Af Context
 * @protected
 * @return {dvt.AfContext} dvt.AfContext object
 */
dvt.SearchPanel.prototype.CreateAfContext = function() {
  var afContext = new dvt.AfContext(this.getCtx(), this._eventManager);
  afContext.setSkinName(this._skinName);

  //TO DO: need this?
  //afContext.setImageCallback(this, this.onAfImageLoaded);
  return afContext;
};


/**
 * Gets callback function to handle scroll event
 * @return {function} callback function to handle scroll event
 */
dvt.SearchPanel.prototype.getScrollEventCallback = function() {
  if (! this._scrollCallbackFunc) {
    var thisRef = this;
    this._scrollCallbackFunc = function(event) {
      if (event.getX() == null)
        return;
      var scrollableContentX = thisRef.getParent().getTranslateX();
      thisRef._textField.updateClipRect(null, null, null, -(scrollableContentX + thisRef._textFieldContainer.getTranslateX() + thisRef._textField.getTranslateX()));
    };
  }
  return this._scrollCallbackFunc;
};


/**
 * @private
 * Gets width for the text field depending on skin and available space
 * @param {number} occupiedWidth width occupied by other elements
 * @param {number} minWidth min width for the text field
 * @return {number} text field width
 */
dvt.SearchPanel.prototype._getTextFieldWidth = function(occupiedWidth, minWidth) {
  var maxWidth = this.IsSkinAlta() ? 200 : 110;
  var availableWidth = .5 * this._callbackObj.getWidth() - occupiedWidth;
  var width = Math.max(availableWidth, minWidth);
  width = Math.min(maxWidth, width);
  return width;
};

/**
 * @protected
 * Render the expanded panel.
 *
 * @return {dvt.Container} a container representing expanded state for the search panel
 */
dvt.SearchPanel.prototype.RenderSelfExpanded = function() {
  var s = new dvt.Container(this.getCtx());

  //Bug 14636627: attach to the display list while rendering
  this.addChild(s);

  this._background = new dvt.Rect(this.getCtx(), 0, 0, 1, 1);
  this._background.setFill(null);
  this._background.setAlpha(this._bgAlpha);
  s.addChild(this._background);

  this._frame = new dvt.Path(this.getCtx());
  this._frame.setFill(null);
  s.addChild(this._frame);


  var padX = this.IsSkinAlta() ? dvt.SearchPanel._ALTA_CONTENT_PADDING : 4;
  var padY = this.IsSkinAlta() ? dvt.SearchPanel._ALTA_CONTENT_PADDING + 7 : 3;
  var goButtonPadX = this.IsSkinAlta() ? 3 : padX + 2;
  var advancedComponentDims = null;

  //create go button and optional link to find available space for the text field
  this._goButton = this.CreateGoButton();

  var afContext = this.CreateAfContext();
  //set keyboard focusable objects
  this._tabStopsNodeArray = [];
  afContext.setTabStopsArray(this._tabStopsNodeArray);
  afContext.registerTabStop(this);
  this._curTabStopIdx = -1;

  if (this._advancedTemplate) {
    this._advancedComponent = dvt.AfComponentFactory.parseAndLayout(afContext, this._advancedTemplate, this._frame);
    if (this._advancedComponent) {
      advancedComponentDims = dvt.DisplayableUtils.getDimensionsForced(this.getCtx(), this._advancedComponent);
    }
  }

  //create text field container and text field
  this._textFieldContainer = new dvt.Rect(this.getCtx(), 0, 0, 1, 1);
  this._textFieldContainer.setAlpha(this._textBgAlpha);
  this._frame.addChild(this._textFieldContainer);

  var occupiedWidth = advancedComponentDims ? advancedComponentDims.w + this._goButtonWidth + padX * 4 + goButtonPadX + 1 : this._goButtonWidth + padX * 3 + goButtonPadX + 1;
  var textFieldWidth = this._getTextFieldWidth(occupiedWidth, this._goButtonWidth);
  this._textField = this.CreateTextField(this._textFieldContainer, textFieldWidth);

  var textFieldContainerDims = dvt.DisplayableUtils.getDimensionsForced(this.getCtx(), this._textFieldContainer);
  //Bug 16901294: save the text field's original dims to use for clipping it below
  var textFieldDims = new dvt.Rectangle(textFieldContainerDims.x, textFieldContainerDims.y, textFieldWidth, textFieldContainerDims.h);
  //text field should be 20 pixels tall, but size may be incorrect for editable dvt.Text
  if (textFieldContainerDims.h < this._textFieldHeight) {
    this._textField.setTranslateY(.5 * (this._textFieldHeight - textFieldContainerDims.h));
    textFieldContainerDims.h = this._textFieldHeight;
  }

  textFieldContainerDims.w = textFieldWidth + padX;
  this._textFieldContainer.setTranslate(padX, padY);
  this._textFieldContainer.setWidth(textFieldContainerDims.w);
  this._textFieldContainer.setHeight(textFieldContainerDims.h);
  this._textFieldContainer.setRx(this._textCornerRadius);
  this._textFieldContainer.setRy(this._textCornerRadius);
  var fill = new dvt.SolidFill(this._textBgColor);
  this._textFieldContainer.setFill(fill);
  if (this._textBorderColor) {
    this._textFieldContainer.setSolidStroke(this._textBorderColor);
  }
  if (this.IsSkinAlta()) {
    this._textField.setTranslateX(this._textFieldContainer.getTranslateX() / 2);
  }

  //position go button
  this._frame.addChild(this._goButton);
  this._goButton.setTranslate(this._textFieldContainer.getTranslateX() + textFieldContainerDims.w + goButtonPadX,
                              (this._textFieldContainer.getTranslateY() + textFieldContainerDims.h + padY - this._goButtonHeight) / 2);

  var maxHeight = Math.max(this._goButton.getTranslateY() + this._goButtonHeight, this._textFieldContainer.getTranslateY() + textFieldContainerDims.h);
  var maxWidth = this._goButton.getTranslateX() + this._goButtonWidth + padX;

  //position the optional advanced link if one is set
  var advancedComponentCalcW;
  if (this._advancedComponent) {
    //TO DO:
    //advancedComponentCalcW = this._advancedComponent.getCalcWidth(advancedComponentDims.w);
    advancedComponentCalcW = advancedComponentDims.w;
    if (this.IsSkinAlta()) {
      this._advancedComponent.setTranslate(this._goButton.getTranslateX() + this._goButtonWidth + padX,
          this._goButton.getTranslateY() + (this._goButtonHeight - advancedComponentDims.h) / 2);
      maxWidth = this._advancedComponent.getTranslateX() + advancedComponentCalcW + padX;
      maxHeight = this._advancedComponent.getTranslateY() + advancedComponentDims.h;
    }
    else {
      this._advancedComponent.setTranslateX(maxWidth);
      if (advancedComponentDims.h < this._textFieldContainer.getTranslateY() + textFieldContainerDims.h + padY) {
        this._advancedComponent.setTranslateY((this._textFieldContainer.getTranslateY() + textFieldContainerDims.h + padY - advancedComponentDims.h) / 2);
      }
      maxHeight = Math.max(maxHeight, advancedComponentDims.h);
      maxWidth = this._advancedComponent.getTranslateX() + advancedComponentCalcW + padX;
    }
    this._frame.addChild(this._advancedComponent);
  }

  // add collapse button and adjust the frame and background
  // do it after we have the maxHeigh, otherwise the collapse button doesn't scale well
  if (!this.IsEmbedded()) {
    this._collapseButton = this.CreateCollapseButton(maxHeight + padY);
    s.addChild(this._collapseButton);
  }

  //render the background
  var collapseButtonWidth = 0;
  if (this._collapseButton)
    collapseButtonWidth = dvt.StyleUtils.getStyle(this._styleMap, dvt.ControlPanel.CP_OPEN_CLOSE_BUTTON_WIDTH, 0);

  this._background.setWidth(maxWidth + 1 + collapseButtonWidth);
  this._background.setHeight(maxHeight + padY + 1);
  this._background.setRx(this._borderRadius);
  this._background.setRy(this._borderRadius);

  if (!this.IsEmbedded()) {
    fill = new dvt.SolidFill(this._bgColor);
    this._background.setFill(fill);
  }

  this.CreateFrame(this._frame, maxWidth, maxHeight + padY, this._borderRadius);
  if (!this.IsEmbedded()) {
    var stroke = new dvt.SolidStroke(this._borderColor, dvt.SearchPanel._BORDER_ALPHA, dvt.SearchPanel._BORDER_THICKNESS);
    this._frame.setStroke(stroke);
  }

  if (this._collapseButton && !dvt.Agent.isRightToLeft(this.getCtx()))
    this._frame.setTranslateX(dvt.StyleUtils.getStyle(this._styleMap, dvt.ControlPanel.CP_OPEN_CLOSE_BUTTON_WIDTH, 0));

  this._expandedSprite = s;

  //BiDi: reverse the order of the components
  if (dvt.Agent.isRightToLeft(this.getCtx())) {
    if (this._collapseButton) {
      this._collapseButton.setTranslateX(maxWidth - this._collapseButton.getTranslateX());
    }
    this._textFieldContainer.setTranslateX(maxWidth - this._textFieldContainer.getTranslateX() - textFieldContainerDims.w);
    this._goButton.setTranslateX(maxWidth - this._goButton.getTranslateX() - this._goButtonWidth);
    if (this._advancedComponent) {
      this._advancedComponent.setTranslateX(maxWidth - this._advancedComponent.getTranslateX() - advancedComponentCalcW);
    }
  }

  this.CheckShowDefaultText();

  //Bug 16901294: clip the dvt.Text because the editable HTML text area will be clipped itself
  var clipPath = new dvt.ClipPath('searchTextClip');
  clipPath.addRect(textFieldDims.x + this._textField.getTranslateX(), textFieldDims.y + this._textField.getTranslateY(),
                   textFieldDims.w, textFieldDims.h);
  this._textField.setClipPath(clipPath);

  //Bug 14636627: remove from the display list after rendering
  this.removeChild(s);

  return s;
};


/**
 * Check whether the text field has been used yet, and if not,
 * mark it as used and hide the default text.
 */
dvt.SearchPanel.prototype.CheckTextFieldUsed = function() {
  if (!this._bTextFieldUsed) {
    this._bTextFieldUsed = true;
    this._textField.setText('');
  }
};

/**
 * Get the diagram associated to the search panel
 * @return {dvt.AdfDiagram} diagram object
 */
dvt.SearchPanel.prototype.getDiagram = function() {
  return this._callbackObj;
};

/**
 * Get the focus out of text field and retain the focus within SVG
 * If user tabs out of the text field the focus should be remain within SVG,
 * otherwise the focus would go to HTML DOM
 * @param {dvt.KeyboardEvent} event  keyboard event
 */
dvt.SearchPanel.prototype.blurEditTextField = function(event) {
  //bring focus back to SVG
  var svg = this.getCtx().getSvgDocument();
  var func = function() {
    svg.parentNode.focus(event);
  };
  this._blurTextField();
  window.setTimeout(func, 0);
};

/**
 * Handle mouse down event in the text field
 * @param {dvt.MouseEvent} event  Mouse event on text field
 */
dvt.SearchPanel.prototype.HandleTextFieldMouseDown = function(event) {
  this.CheckTextFieldUsed();

  this._eventManager.setFocus(this._textFieldProxyObj);
  this._textField.focusEditText();

  //mark as focused
  this._bFocus = true;
  //this.AddStageFocusChangeListeners();

  //Activate the keyboard handler of the search panel
  this.getDiagram().enableSearchKeyboardHandler();
};

/**
 * Handle keyboard event in text field
 * ENTER - fires search event for the entered text
 * TAB - Focuses the navigation the first search result
 * ESCAPE - collapses search panel (if results panel is open, results panel is collapsed)
 * @param {dvt.KeyboardEvent} event  Keyboard event
 * @return {boolean} true if the event is handled
 */
dvt.SearchPanel.prototype.HandleTextFieldKeyDown = function(event) {
  var bConsume = false;

  //ENTER = fire search event
  if (event.keyCode == dvt.KeyboardEvent.ENTER && this.isTextFieldFocus()) {
    this.HideResultsAndFireSearchEvent();
    bConsume = true;
  } else if (event.keyCode == dvt.KeyboardEvent.ESCAPE || event.keyCode == dvt.KeyboardEvent.TAB) {
    //Allow only ESCAPE and TAB events in text field
    bConsume = this.HandleKeyDownSelf(event);
  }
  return bConsume;
};

/**
 * Check if the text field is in focus
 * @return {boolean} true if the text field is in focus
 */
dvt.SearchPanel.prototype.isTextFieldFocus = function() {
  return this._eventManager.getFocus() == this._textFieldProxyObj;
};

/**
 * Handler for the search/go button
 * @param {DvtEvent} click event
 */
dvt.SearchPanel.prototype.HandleGoClick = function(event) {
  //Bug 17369763 - blur event is not called on android chrome when user clicks on go button (textarea should be destroyed on blur)
  if (dvt.Agent.isTouchDevice() && dvt.Agent.isBrowserChrome() && this._textField && this._textField.isEditing())
    this._textField.destroy();
  this.HideResultsAndFireSearchEvent();
};

dvt.SearchPanel.prototype.HideResultsAndFireSearchEvent = function() {
  // Bug 08892379 - can't search for "search" in hv
  // if (this._textField.getText() != this._defaultText)
  if (this._bTextFieldUsed) {
    var t = this.HideSearchResultsPanel(true);
    if (t) {
      var thisRef = this;
      var func = function() {
        thisRef.FireSearchEvent(thisRef._textField.getText());
      };
      dvt.Playable.appendOnEnd(t, func);
      t.play();
    }
    else {
      this.FireSearchEvent(this._textField.getText());
    }
  }
};


/**
 * Handle a keyboard event on the search panel other than textfield
 *
 * @param {dvt.KeyboardEvent} event  keyboard event
 * @return {boolean} true if the event is handled
 */
dvt.SearchPanel.prototype.HandleKeyDownSelf = function(event) {
  var bConsume = false;
  if (event.keyCode == dvt.KeyboardEvent.F && event.ctrlKey) {
    this.getDiagram().FocusSearchField();
    bConsume = true;
  }
  //ESC = collapse
  else if (event.keyCode == dvt.KeyboardEvent.ESCAPE) {
    bConsume = this.HandleKeyboardEscape(event);
  }
  //TAB = focus on additional navigable object/links or the search results or navigate out
  else if (event.keyCode == dvt.KeyboardEvent.TAB) {
    var navigable = this.getNextNavigable(event);
    if (navigable) {
      if (navigable instanceof dvt.SearchPanel) {
        this.focusTextField(event);
        bConsume = true;
      }
      else if (navigable instanceof DvtSearchResultsPanel && this._searchResultsPanel && this._searchResultsPanel.getVisible()) {
        bConsume = this.HandleSearchResultsKeyDown(event);
      }
      else if (navigable.showKeyboardFocusEffect) {
        navigable.showKeyboardFocusEffect();
        this._eventManager.UpdateActiveElement(navigable, navigable.getDisplayObj());
        bConsume = true;
      }
    }
  }
  else {
    var navigable = this.getCurrentTabStop();
    if (navigable) {
      if (event.keyCode == dvt.KeyboardEvent.ENTER && navigable.handleKeyboardEvent) {
        if (navigable.hideKeyboardFocusEffect)
          navigable.hideKeyboardFocusEffect();
        navigable.handleKeyboardEvent(event);
        bConsume = true;
      }
      else {
        bConsume = this.HandleSearchResultsKeyDown(event);
      }
    }
  }
  return bConsume;
};

/**
 * Handle keyboard escape event
 * If the search results panel is open, it is closed on escape
 * If the results panel is not open, the search panel is collapsed
 * @param {dvt.KeyboardEvent} event  keyboard event
 * @return {boolean} true if the event is handled
 */
dvt.SearchPanel.prototype.HandleKeyboardEscape = function(event) {
  this.resetTabStops();
  //Close the search results panel if open
  if (this._searchResultsPanel && this._searchResultsPanel.getVisible()) {
    this.getDiagram().FocusSearchField();
    var t = this.HideSearchResultsPanel(true);
    if (t)
      t.play();
    return true;
  }

  if (this._state == dvt.SearchPanel.STATE_EMBEDDED) {
    //collapse the search panel, if search panel is open and results panel is not open
    this.blurEditTextField(event);
    this.getDiagram().collapseSearchPanel();
  } else if (this._state == dvt.SearchPanel.STATE_EXPANDED) {
    //collapse
    this.blurEditTextField(event);
    this._doCollapse();
  }
  //After collapsing the panel, the focus should go to diagram.
  this.getDiagram().enableDiagramKeyboardHandler();
  return false;
};

/**
 * Handle search results keyboard events
 * @param {dvt.KeyboardEvent} event  keyboard event
 * @return {boolean} true if the event is handled
 */
dvt.SearchPanel.prototype.HandleSearchResultsKeyDown = function(event) {
  if (this._searchResultsPanel) {
    //Handle only Tab, Enter, Up/Down events in search results panel
    if (event.keyCode == dvt.KeyboardEvent.TAB ||
        event.keyCode == dvt.KeyboardEvent.ENTER ||
        event.keyCode == dvt.KeyboardEvent.UP_ARROW ||
        event.keyCode == dvt.KeyboardEvent.DOWN_ARROW) {
      return this._searchResultsPanel.handleKeyboardEvent(event);
    }
  }
  //returning true ignores other keyboard events in the search results panel
  return true;
};

/**
 * Create Text field
 * @protected
 * @param {dvt.Rect} parent parent container for the text field
 * @param {number} width text field width
 * @return {dvt.Text} text field
 */
dvt.SearchPanel.prototype.CreateTextField = function(parent, width) {
  //temporarily render a rect just to show something
  //var textField = new dvt.Rect(this.getCtx(), 0, 0, 110, 20);
  //var fill = new dvt.SolidFill("#ffffff", 0);
  //textField.setFill(fill);
  //TO DO:
  var textField = new dvt.Text(this.getCtx(), this._text);
  //need to add text field to display list before setting edit mode
  if (parent) {
    parent.addChild(textField);
  }
  //set other props before setting edit mode
  textField.setSolidFill(dvt.SearchPanel._TEXT_COLOR);
  if (this.IsSkinAlta()) {
    var _textFont = '\"Helvetica Neue\", Helvetica, Arial, sans-serif';
    var _textSize = 12;
    var _textColor = '#333333';
    var fill = new dvt.SolidFill(_textColor, 1);
    textField.setFill(fill);
    textField.setFontSize(_textSize);
    var cssStyle = new dvt.CSSStyle(dvt.CSSStyle.FONT_FAMILY + ':' + _textFont + ';' + dvt.CSSStyle.BACKGROUND + ':' + this._textBgColor + ';');
    textField.setCSSStyle(cssStyle);
  }
  textField.setEditWidth(width);
  var thisRef = this;
  //Bug 16901294: pass the function to call when editing ends
  textField.setEditMode(true, null, function(editedText) {thisRef._blurTextField(); return null;});

  var proxy = {};
  this._textFieldProxyObj = proxy;
  //Bug 17157597: on touch device, listen for click event instead of mousedown
  //if (dvt.Agent.isTouchDevice()) {
  proxy.HandleClick = function(event) {
    thisRef.HandleTextFieldMouseDown(event);
  };
  //}
  //else {
  //  proxy.HandleMouseDown = function(event) {
  //    thisRef.HandleTextFieldMouseDown(event);
  //  };
  //}
  proxy.showKeyboardFocusEffect = function() {
    //  thisRef.CheckTextFieldUsed();
    proxy.bFocusEffect = true;
    //  console.log("show textField focus");
  };
  proxy.hideKeyboardFocusEffect = function() {
    //  thisRef.CheckShowDefaultText();
    proxy.bFocusEffect = false;
    //  console.log("hide textField focus");
  };
  proxy.isShowingKeyboardFocusEffect = function() {
    return proxy.bFocusEffect;
  };
  proxy.getNextNavigable = function(event) {
    //  if (event.type == dvt.MouseEvent.CLICK ||
    //      event.charCode == 'f' && event.ctrlKey) {
    return proxy;
    //  }
  };
  //proxy.getKeyboardBoundingBox = function() {
  //  //return the bounding box for the text field, in stage coordinates
  //  return thisRef._textField.getDimensions(thisRef.getCtx().getStage());
  //};
  //Bug 16901294: event manager gets displayable from logical object to use for WAI-ARIA
  proxy.getDisplayable = function() {
    return textField;
  };
  this._eventManager.associate(textField, proxy);

  return textField;
};

/**
 * Handle kebyoard event on the search panel
 * @param {dvt.KeyboardEvent} event search panel keyboard event
 * @return {boolean} true if the event is handled
 */
dvt.SearchPanel.prototype.handleKeyboardEvent = function(event) {
  var bConsume = false;
  if (!this.getDiagram().isSearchPanelDisclosed())
    return bConsume;

  //Bug 16901294: check whether the text field has focus
  //TO DO: the text field never loses focus once gained, so need to figure out how to force that
  //if (this._textFieldProxyObj.isShowingKeyboardFocusEffect()) {
  if (this.isTextFieldFocus()) {
    bConsume = this.HandleTextFieldKeyDown(event);
  } else {
    bConsume = this.HandleKeyDownSelf(event);
  }

  if (bConsume) {
    event.preventDefault();
    event.stopPropagation();
  }
  return bConsume;
};


/**
 * Handle a focusOut event on the text field.
 *
 * @param event FocusEvent
 */
dvt.SearchPanel.prototype.HandleTextFieldFocusOut = function(event) {
  this.CheckShowDefaultText();
};


/**
 * Check whether the text field should show default text.
 */
dvt.SearchPanel.prototype.CheckShowDefaultText = function() {
  if (this._textField) {
    //if the text in the field is null or empty, show default text
    if (this._textField.getText() == null || this._textField.getText() == '') {
      //show default text
      if (!this.IsSkinAlta()) { //Don't set default text for alta skin
        this._textField.setText(this._defaultText);
      }
      //reset the flag indicating whether the text field has been used
      this._bTextFieldUsed = false;
    }
  }
};

/**
 * Resize a given sprite that was originally drawn for the control panel.
 *
 * @param s sprite that was originally drawn for control panel LAF
 */
//dvt.SearchPanel.ResizeControlPanelSprite = function(s) {
//  //set height because search panel is shorter than control panel
//  s.height = 26;
//};


/**
 * @protected
 * Create the expand button.
 *
 * @return {dvt.Container} expand button
 */
dvt.SearchPanel.prototype.CreateExpandButton = function() {
  var bBiDi = dvt.Agent.isRightToLeft(this.getCtx());

  var hh = dvt.ControlPanelLAFUtils.getViewPanelHalfHeight();
  var button = dvt.ButtonLAFUtils.createExpandButton(this.getCtx(), this._imageURIs, hh, this._styleMap,
      bBiDi ? dvt.ButtonLAFUtils.DIR_LEFT : dvt.ButtonLAFUtils.DIR_RIGHT);

  var container = new dvt.Container(this.getCtx());
  container.addChild(button);
  button = container;

  var thisRef = this;
  var proxy = {};
  proxy.HandleClick = function(event) {
    thisRef.HandleExpandClick(event);
  };
  proxy.getTooltip = function() {
    return thisRef._expandTooltip;
  };
  this._eventManager.associate(button, proxy);
  return button;
};


/**
 * @protected
 * Create the collapse button.
 *
 * @return {dvt.Container} collapse button
 */
dvt.SearchPanel.prototype.CreateCollapseButton = function(hh) {
  var bBiDi = dvt.Agent.isRightToLeft(this.getCtx());

  if (!hh)
    hh = dvt.ControlPanelLAFUtils.getViewPanelHalfHeight();
  var button = dvt.ButtonLAFUtils.createCollapseButton(this.getCtx(), this._imageURIs, hh, this._styleMap,
      bBiDi ? dvt.ButtonLAFUtils.DIR_LEFT : dvt.ButtonLAFUtils.DIR_RIGHT);

  var container = new dvt.Container(this.getCtx());
  container.addChild(button);
  button = container;

  var thisRef = this;
  var proxy = {};
  proxy.HandleClick = function(event) {
    thisRef.HandleCollapseClick(event);
  };
  proxy.getTooltip = function() {
    return thisRef._collapseTooltip;
  };
  this._eventManager.associate(button, proxy);
  return button;
};

/**
 * Handle a click on the expand button.
 *
 * @param {dvt.MouseEvent} event
 */
dvt.SearchPanel.prototype.HandleExpandClick = function(event) {
  event.stopPropagation();

  this._doExpand();
};


/**
 * Handle a click on the collapse button.
 *
 * @param {dvt.MouseEvent} event MouseEvent
 */
dvt.SearchPanel.prototype.HandleCollapseClick = function(event) {
  event.stopPropagation();

  this._doCollapse();
};


/**
 * Create the go button.
 * @protected
 * @return {dvt.Button} go button
 */
dvt.SearchPanel.prototype.CreateGoButton = function() {
  var button;
  if (this.IsSkinAlta()) {
    button = this._createAltaGoButton();
  }
  else {
    button = dvt.ButtonLAFUtils.createQuickQueryButton(this.getCtx(), this._imageURIs);
  }

  var thisRef = this;
  var proxy = {};
  proxy.HandleClick = function(event) {
    thisRef.HandleGoClick(event);
  };
  proxy.getTooltip = function() {
    return thisRef._goTooltip;
  };
  this._eventManager.associate(button, proxy);
  return button;
};


/**
 * Create Go Button with Alta Skin
 * @private
 * @return {dvt.Button}  Button with Alta Skin
 */
dvt.SearchPanel.prototype._createAltaGoButton = function() {
  var hh = this._textFieldHeight;
  var text = this._defaultText;
  var labelStyle = this.getLabelStyle();
  var textEna = new dvt.Text(this.getCtx(), text, 0, 0);
  var textOvr = new dvt.Text(this.getCtx(), text, 0, 0);
  var textDwn = new dvt.Text(this.getCtx(), text, 0, 0);
  if (labelStyle) {
    textEna.setCSSStyle(labelStyle[0]);
    textOvr.setCSSStyle(labelStyle[1]);
    textDwn.setCSSStyle(labelStyle[2]);
  }

  var buttonStyle = this.getButtonStyle();
  var ena = new dvt.Rect(this.getCtx(), 0, 0, ww, hh);
  ena.setRx(dvt.SearchPanel._ALTA_BUTTON_RADIUS);
  ena.setRy(dvt.SearchPanel._ALTA_BUTTON_RADIUS);

  var textDims = textEna.measureDimensions();
  var pad = 7;
  var ww = textDims.w + 2 * pad;

  //save go button width because it hasn't been initialized yet
  this._goButtonWidth = ww;

  var ena = new dvt.Rect(this.getCtx(), 0, 0, ww, hh);
  ena.setRx(dvt.SearchPanel._ALTA_BUTTON_RADIUS);
  ena.setRy(dvt.SearchPanel._ALTA_BUTTON_RADIUS);
  textEna.setX(pad);
  textEna.setY(.5 * (hh - textDims.h));
  ena.addChild(textEna);

  var ovr = new dvt.Rect(this.getCtx(), 0, 0, ww, hh);
  ovr.setRx(dvt.SearchPanel._ALTA_BUTTON_RADIUS);
  ovr.setRy(dvt.SearchPanel._ALTA_BUTTON_RADIUS);
  textOvr.setX(pad);
  textOvr.setY(.5 * (hh - textDims.h));
  ovr.addChild(textOvr);

  var dwn = new dvt.Rect(this.getCtx(), 0, 0, ww, hh);
  dwn.setRx(dvt.SearchPanel._ALTA_BUTTON_RADIUS);
  dwn.setRy(dvt.SearchPanel._ALTA_BUTTON_RADIUS);
  textDwn.setX(pad);
  textDwn.setY(.5 * (hh - textDims.h));
  dwn.addChild(textDwn);

  if (buttonStyle) {
    dvt.ButtonLAFUtils.parseStyle(ena, buttonStyle[0]);
    dvt.ButtonLAFUtils.parseStyle(ovr, buttonStyle[1]);
    dvt.ButtonLAFUtils.parseStyle(dwn, buttonStyle[2]);
  }

  var button = new dvt.Button(this.getCtx(), ena, ovr, dwn);
  return button;
};


dvt.SearchPanel.prototype.LoadIcon = function(bname, t, stat) {
  if (!stat && stat != 0) {
    stat = 9999;
  }

  //Bug 12353637 - nls:bidi: organization chart should support bidi format
  var src = dvt.ImageLAFUtils.getIconUrl(bname, stat);

  //create a new Loader object to load the image
  var loader = dvt.ImageLAFUtils.loadIcon(this.getCtx(), src);
  loader.setMouseEnabled(false);

  t.addChild(loader);
  return loader;
};

dvt.SearchPanel.prototype.parseNode = function(xmlNode) {
  var nodeName = xmlNode.getName();
  switch (nodeName) {
    case dvt.SearchPanel.ELEM_SEARCH_RESULTS_TEMPLATE:
      this.ParseResultsTemplate(xmlNode);
      break;
    case dvt.SearchPanel.ELEM_SEARCH_ADVANCED_TEMPLATE:
      this.ParseAdvancedTemplate(xmlNode);
      break;
    case dvt.SearchPanel.ELEM_SEARCH_RESULTS_DATA:
      this.ParseResultsData(xmlNode);
      this._bShowSearchResults = true;
      break;
  }
};


/**
 * Parse the JSON Node for search
 * @param {object} jsonObj  JSON Object to parse
 */
dvt.SearchPanel.prototype.parseNodeJson = function(jsonObj) {
  if (jsonObj) {
    var s = jsonObj[dvt.SearchPanel.ELEM_SEARCH_RESULTS_TEMPLATE];
    if (s)
      this.ParseResultsTemplateJson(s);
    s = jsonObj[dvt.SearchPanel.ELEM_SEARCH_ADVANCED_TEMPLATE];
    if (s)
      this.ParseAdvancedTemplateJson(s);
    s = jsonObj[dvt.SearchPanel.ELEM_SEARCH_RESULTS_DATA];
    if (s) {
      this.ParseResultsDataJson(s);
      this._bShowSearchResults = true;
    }
  }
};

dvt.SearchPanel.prototype.parseResultsData = function(xmlNode, searchText, searchType, clientRowKey) {
  //call parser
  this.ParseResultsData(xmlNode);
  this._bShowSearchResults = true;

  //if there's no clientRowKey specified, then this is the initial list
  //of results
  if (!clientRowKey || clientRowKey == '-1') {
    this.ProcessInitialSearchResults();
  }
  else {
    this.ProcessAdditionalSearchResults();
  }
};


/**
 * Load search results JSON data
 * @param {object} jsonObj  JSON search results data
 * @param {String} searchText  Search text
 * @param {String} searchType  Search Type
 * @param {String} clientRowKey  If no client row key is specified, then it is initial list
 * @protected
 */
dvt.SearchPanel.prototype.parseResultsDataJson = function(jsonObj, searchText, searchType, clientRowKey) {
  //call parser
  this.ParseResultsDataJson(jsonObj);
  this._bShowSearchResults = true;

  //if there's no clientRowKey specified, then this is the initial list
  //of results
  if (!clientRowKey || clientRowKey == '-1') {
    this.ProcessInitialSearchResults();
  }
  else {
    this.ProcessAdditionalSearchResults();
  }
};

dvt.SearchPanel.prototype.ProcessInitialSearchResults = function() {
  this.DisplaySearchResults();
};

/**
 * Process additional search results
 */
dvt.SearchPanel.prototype.ProcessAdditionalSearchResults = function() {
  //set the focus index in result panel to auto-scroll to the selected item
  var focusIndex = this._searchResultsPanel.getFocusItemIndex();
  this.DisplaySearchResults(true);
  this._searchResultsPanel.setFocusItemIndex(focusIndex);
  //this.AddSearchResults(items, total);
};

dvt.SearchPanel.prototype.ParseResultsTemplate = function(xmlNode) {
  var s;

  s = xmlNode.getAttr(dvt.SearchPanel._ATTR_EMPTY_TEXT);
  if (s) {
    this.setSearchResultsEmptyText(s);
  }

  s = xmlNode.getAttr(dvt.SearchPanel._ATTR_INITIAL_BEHAVIOR);
  if (s) {
    this.setInitialBehavior(s);
  }

  var childNodes = xmlNode.getChildNodes();
  for (var i = 0; i < childNodes.length; i++) {
    var node = childNodes[i];
    if (node.getName() == dvt.SearchPanel._ELEM_NODE) {
      this.ParseResultsNode(node);
    }
  }
};


/**
 * Parse the results template in JSON
 * @param {object} jsonObj  template to parse
 */
dvt.SearchPanel.prototype.ParseResultsTemplateJson = function(jsonObj) {
  var s;

  s = jsonObj[dvt.SearchPanel._ATTR_EMPTY_TEXT];
  if (s) {
    this.setSearchResultsEmptyText(s);
  }

  s = jsonObj[dvt.SearchPanel._ATTR_INITIAL_BEHAVIOR];
  if (s) {
    this.setInitialBehavior(s);
  }
  //Node data
  s = jsonObj[dvt.SearchPanel._ELEM_NODE];
  this.ParseResultsNodeJson(s);
};


dvt.SearchPanel.prototype.ParseAdvancedTemplate = function(xmlNode) {
  var template = dvt.AfComponentFactory.parseXml(xmlNode);
  if (template) {
    this._advancedTemplate = template;
  }
  else {
    this._advancedTemplate = null;
  }
};


/**
 * Parse the results Advanced template in JSON
 * @param {object} jsonObj  Advanced template to parse
 */
dvt.SearchPanel.prototype.ParseAdvancedTemplateJson = function(jsonObj) {
  var templateJson = jsonObj['children'];
  var template = dvt.AfComponentFactory.parseJson(templateJson);
  if (template) {
    this._advancedTemplate = template;
  }
  else {
    this._advancedTemplate = null;
  }
};

dvt.SearchPanel.prototype.ParseResultsData = function(xmlNode) {
  var s;

  s = xmlNode.getAttr(dvt.SearchPanel._ATTR_TOTAL);
  this._searchResultsTotal = (s ? parseInt(s) : -1);

  s = xmlNode.getAttr(dvt.SearchPanel._ATTR_SEARCH_TEXT);
  this._searchText = s;

  s = xmlNode.getAttr(dvt.SearchPanel._ATTR_SEARCH_RESULTS_FIRST_VISIBLE);
  this._searchResultsFirstVisible = (s ? parseInt(s) : -1);

  s = xmlNode.getAttr(dvt.SearchPanel._ATTR_SEARCH_RESULTS_LAST_VISIBLE);
  this._searchResultsLastVisible = (s ? parseInt(s) : -1);

  s = xmlNode.getAttr(dvt.SearchPanel._ATTR_SEARCH_RESULTS_SELECTED);
  this._searchResultsSelected = (s ? parseInt(s) : -1);

  var childNodes = xmlNode.getChildNodes();
  for (var i = 0; i < childNodes.length; i++) {
    var child = childNodes[i];
    if (child.getName() == dvt.SearchPanel._ELEM_NODE_DATA) {
      this.ParseNodeData(child);
    }
  }
};


/**
 * Parse the results data in JSON
 * @param {object} jsonObj  Results data
 */
dvt.SearchPanel.prototype.ParseResultsDataJson = function(jsonObj) {
  var s;

  s = jsonObj[dvt.SearchPanel._ATTR_TOTAL];
  this._searchResultsTotal = (!isNaN(s) ? parseInt(s) : -1);

  s = jsonObj[dvt.SearchPanel._ATTR_SEARCH_TEXT];
  this._searchText = s;

  s = jsonObj[dvt.SearchPanel._ATTR_SEARCH_RESULTS_FIRST_VISIBLE];
  this._searchResultsFirstVisible = (!isNaN(s) ? parseInt(s) : -1);

  s = jsonObj[dvt.SearchPanel._ATTR_SEARCH_RESULTS_LAST_VISIBLE];
  this._searchResultsLastVisible = (!isNaN(s) ? parseInt(s) : -1);

  s = jsonObj[dvt.SearchPanel._ATTR_SEARCH_RESULTS_SELECTED];
  this._searchResultsSelected = (!isNaN(s) ? parseInt(s) : -1);

  var nodeList = jsonObj[dvt.SearchPanel._ELEM_NODE_DATA];
  if (nodeList) {
    for (var i = 0; i < nodeList.length; i++) {
      this.ParseNodeDataJson(nodeList[i]);
    }
  }
};

dvt.SearchPanel.prototype.ParseNodeData = function(xmlNode) {
  var s;
  var nodeData = new DvtSearchResultsNodeData();

  s = xmlNode.getAttr(dvt.SearchPanel._ATTR_ID);
  nodeData.setId(s);

  var childNode = xmlNode.getFirstChild();
  if (childNode && childNode.getName() === dvt.SearchPanel._ELEM_EL)
  {
    var arAttrs = childNode.getAttributes();
    var attr;
    var attrs = {};
    for (var i = 0; i < arAttrs.length; i++)
    {
      attr = arAttrs[i];
      attrs[attr.name] = attr.value;
    }
    nodeData.setElAttributes(attrs);
  }

  this.AddResultNodeData(nodeData);
};


/**
 * Parse the node data in JSON
 * @param {object} jsonObj  JSON node data
 */
dvt.SearchPanel.prototype.ParseNodeDataJson = function(jsonObj) {
  var s;
  var nodeData = new DvtSearchResultsNodeData();

  s = jsonObj[dvt.SearchPanel._ATTR_ID];
  nodeData.setId(s);

  var elAttrs = jsonObj[dvt.SearchPanel._ELEM_EL];
  nodeData.setElAttributes(elAttrs);

  this.AddResultNodeData(nodeData);
};

dvt.SearchPanel.prototype.AddResultNodeData = function(nodeData) {
  if (!this._arSearchResultsNodeData) {
    this._arSearchResultsNodeData = [];
  }

  this._arSearchResultsNodeData.push(nodeData);
};

dvt.SearchPanel.prototype.ClearResultsNodeData = function() {
  if (this._arSearchResultsNodeData) {
    while (this._arSearchResultsNodeData.length > 0) {
      this._arSearchResultsNodeData.pop();
    }
    this._arSearchResultsNodeData = null;
  }
};

dvt.SearchPanel.prototype.ParseResultsNode = function(xmlNode) {
  var afRoot = new dvt.AfPanelGroupLayout();
  afRoot.setLayout(dvt.AfPanelGroupLayout.LAYOUT_VERTICAL);
  this.ParseNodeAttrs(xmlNode, afRoot);

  var childNodes = xmlNode.getChildNodes();
  if (childNodes && childNodes.length > 0) {
    var template;
    for (var i = 0; i < childNodes.length; i++) {
      var tmpNode = childNodes[i];
      if (tmpNode.getName() === dvt.SearchPanel._ELEM_F) {
        var nameAttr = tmpNode.getAttr(dvt.SearchPanel._ATTR_AF_NAME);
        if (nameAttr === 'content') {
          template = dvt.AfComponentFactory.parseXml(tmpNode);
          if (template) {
            afRoot.addChild(template);
          }
        }

        //when removing child node, need to decrement i to compensate
        childNodes.splice(i, 1);
        i--;
      }
    }
  }

  this._resultsTemplate = afRoot;
};


/**
 * Parse the results node in JSON
 * @param {object} jsonObj  JSON object to parse
 * @protected
 */
dvt.SearchPanel.prototype.ParseResultsNodeJson = function(jsonObj) {
  var afRoot = new dvt.AfPanelGroupLayout();
  afRoot.setLayout(dvt.AfPanelGroupLayout.LAYOUT_VERTICAL);
  this.ParseNodeAttrsJson(jsonObj, afRoot);

  var facetJson = jsonObj[dvt.SearchPanel._ELEM_F];
  var nameAttr = facetJson[dvt.SearchPanel._ATTR_AF_NAME];
  if (nameAttr === 'content') {
    var contentList = facetJson['children'];
    var template = dvt.AfComponentFactory.parseJson(contentList);
    if (template) {
      afRoot.addChild(template);
    }
  }
  this._resultsTemplate = afRoot;
};

dvt.SearchPanel.prototype.ParseNodeAttrs = function(xmlNode, afRoot) {
  var s;

  s = xmlNode.getAttr(dvt.SearchPanel._ATTR_ID);
  afRoot.setId(s);

  var inlineStyle = '';

  var ww = xmlNode.getAttr(dvt.SearchPanel._ATTR_WIDTH);
  if (ww) {
    if (inlineStyle.length > 0) {
      inlineStyle += ';';
    }
    inlineStyle += 'width:' + ww;
  }

  var hh = xmlNode.getAttr(dvt.SearchPanel._ATTR_HEIGHT);
  if (hh) {
    if (inlineStyle.length > 0) {
      inlineStyle += ';';
    }
    inlineStyle += 'height:' + hh;
  }

  if (inlineStyle.length > 0) {
    afRoot.setInlineStyle(inlineStyle);
  }
};


/**
 * Parse the node attributes in JSON
 * @param {object} jsonObj  JSON node attriutes
 * @param {dvt.AfPanelGroupLayout} afRoot  Panel Group Layout
 */
dvt.SearchPanel.prototype.ParseNodeAttrsJson = function(jsonObj, afRoot) {
  var s;

  s = jsonObj[dvt.SearchPanel._ATTR_ID];
  afRoot.setId(s);

  var inlineStyle = '';

  var ww = jsonObj[dvt.SearchPanel._ATTR_WIDTH];
  if (ww) {
    if (inlineStyle.length > 0) {
      inlineStyle += ';';
    }
    inlineStyle += 'width:' + ww;
  }

  var hh = jsonObj[dvt.SearchPanel._ATTR_HEIGHT];
  if (hh) {
    if (inlineStyle.length > 0) {
      inlineStyle += ';';
    }
    inlineStyle += 'height:' + hh;
  }

  if (inlineStyle.length > 0) {
    afRoot.setInlineStyle(inlineStyle);
  }
};


/**
 * @override
 */
dvt.SearchPanel.prototype.getDimensions = function(targetCoordinateSpace) {
  //Bug 15839275: remove the results before getting dimensions, and then get dimensions
  //of the results directly in order to take their scrollable container clipping into account,
  //and then return the union of both sets of dims
  /*
  var resultsParent = null;
  var resultsIndex = -1;
  var resultsDims = null;
  if (this._searchResultsPanel) {
    resultsParent = this._searchResultsPanel.getParent();
    if (resultsParent) {
      resultsIndex = resultsParent.getChildIndex(this._searchResultsPanel);
      resultsDims = this._searchResultsPanel.getDimensions();
      resultsParent.removeChild(this._searchResultsPanel);
    }
  }

  var dims = dvt.SearchPanel.superclass.getDimensions.call(this);

  if (resultsParent && resultsIndex >= 0) {
    resultsParent.addChildAt(this._searchResultsPanel, resultsIndex);

    if (resultsDims && dims) {
      dims.h += resultsDims.h;
      dims.w = Math.max(dims.w, resultsDims.w);
    }
  }
  */

  var dims;
  if (this._searchResultsPanel) {
    dims = this._searchResultsPanel.getDimensions();
    if (dims) {
      dims.h += dims.y + this._searchResultsPanel.getTranslateY();
    }
  }
  if (! dims) {
    dims = dvt.SearchPanel.superclass.getDimensions.call(this);
  }
  return targetCoordinateSpace && dims ? this.ConvertCoordSpaceRect(dims, targetCoordinateSpace) : dims;
};


/**
 * @override
 */
dvt.SearchPanel.prototype.getDimensionsWithStroke = function(targetCoordinateSpace) {
  return this.getDimensions(targetCoordinateSpace);
};


dvt.SearchPanel.prototype.hideKeyboardFocusEffect = function() {
  //Bug 16901294: The Diagram event manager tries to call this sometimes after clicking outside the
  //search box, so this stub prevents a JS error if the function isn't found.
};

/**
 * @protected
 * Create search panel frame
 * @param {dvt.Path} frame  search panel frame
 * @param {number}  width  frame width
 * @param {number}  height frame height
 * @param {number}  r      border radius
 */
dvt.SearchPanel.prototype.CreateFrame = function(frame, width, height, r) {
  var arPoints;
  var bidi = dvt.Agent.isRightToLeft(this.getCtx());
  var embedded = this.IsEmbedded();
  if (!embedded && !bidi) { //right corners are rounded, left corners are square
    arPoints = ['M', width - r, 0,
                'A', r, r, 0, 0, 1, width, r,
                'L', width, height - r,
                'A', r, r, 0, 0, 1, width - r, height,
                'L', 0, height,
                'L', 0, 0,
                'Z'];
  }
  else if (!embedded && bidi) { //left corners are rounded, right corners are square
    arPoints = ['M', width, 0,
                'L', width, height,
                'L', r, height,
                'A', r, r, 0, 0, 1, 0, height - r,
                'L', 0, r,
                'A', r, r, 0, 0, 1, r, 0,
                'Z'];
  }
  else { // embedded - all corners are rounded
    arPoints = ['M', width - r, 0,
                'A', r, r, 0, 0, 1, width, r,
                'L', width, height - r,
                'A', r, r, 0, 0, 1, width - r, height,
                'L', r, height,
                'A', r, r, 0, 0, 1, 0, height - r,
                'L', 0, r,
                'A', r, r, 0, 0, 1, r, 0,
                'Z'];
  }
  frame.setCommands(arPoints);
};


/**
 * Returns whether the panel can be reused instead of rerendering
 * @return {boolean} true if the panel can be reused
 */
dvt.SearchPanel.prototype.canReuse = function() {
  var ret = false;
  if (dvt.Agent.isTouchDevice() && dvt.Agent.isBrowserChrome() && this._textField && this._textField.isEditing())
    ret = true;
  return ret;
};


/**
 * Returns the dvt.EventManager for this control.
 * @return {dvt.EventManager}
 */
dvt.SearchPanel.prototype.getEventManager = function() {
  return this._eventManager;
};


/**
 * Gets next tab stop for the search panel - itself, af:component from additional content or search results panel
 * @return {dvt.SearchPanel|dvt.AfComponent|DvtSearchResultsPanel}  dvt.SearchPanel, af:component from additional content or search results panel
 */
dvt.SearchPanel.prototype.getNextTabStop = function() {
  this._prevTabStopIdx = this._curTabStopIdx;
  this._curTabStopIdx++;

  if (this._tabStopsNodeArray && this._tabStopsNodeArray.length > this._curTabStopIdx)
    return this._tabStopsNodeArray[this._curTabStopIdx];
  else {
    this._curTabStopIdx = -1;
    return null;
  }
};


/**
 * Gets previous tab stop for the search panel -  af:component from additional content or search results panel
 * @return {dvt.SearchPanel|dvt.AfComponent|DvtSearchResultsPanel}  dvt.SearchPanel, af:component from additional content or search results panel
 */
dvt.SearchPanel.prototype.getPrevTabStop = function() {
  if (this._curTabStopIdx == -1 && this._prevTabStopIdx == this._tabStopsNodeArray.length - 1) {
    //going back to component from outside
    this._curTabStopIdx = this._prevTabStopIdx;
  }
  else {
    this._prevTabStopIdx = this._curTabStopIdx;
    this._curTabStopIdx--;
  }

  if (this._curTabStopIdx >= 0 && this._tabStopsNodeArray && this._tabStopsNodeArray.length > this._curTabStopIdx)
    return this._tabStopsNodeArray[this._curTabStopIdx];
  else {
    this._curTabStopIdx = -1;
    return null;
  }
};

/**
 * Gets current tab stop for the search panel
 * @return {dvt.SearchPanel|dvt.AfComponent|DvtSearchResultsPanel}  dvt.SearchPanel, af:component from additional content or search results panel
 */
dvt.SearchPanel.prototype.getCurrentTabStop = function() {
  if (this._curTabStopIdx > -1 && this._tabStopsNodeArray && this._tabStopsNodeArray.length > this._curTabStopIdx)
    return this._tabStopsNodeArray[this._curTabStopIdx];
  else
    return null;
};


/**
 * Sets current tab stop given the navigable object that belongs to the search panel
 * @param {object} obj text field, af:component from additional content or one of the search results
 */
dvt.SearchPanel.prototype.setCurrentTabStop = function(obj) {
  if (obj == this._textFieldProxyObj)
    obj = this;
  else if (obj instanceof dvt.ItemListSprite)
    obj = this._searchResultsPanel;

  var index = dvt.ArrayUtils.getIndex(this._tabStopsNodeArray, obj);
  if (index >= 0 && this._curTabStopIdx != index) {
    var currNavigable = this.getCurrentTabStop();
    if (currNavigable && currNavigable.hideKeyboardFocusEffect)
      currNavigable.hideKeyboardFocusEffect();
    this._curTabStopIdx = index;
  }
};


/**
 * @private
 * Remove the navigable object from the tab stop array
 * @param {Object} obj  Navigable object that needs to be removed
 */
dvt.SearchPanel.prototype._removeTabStop = function(obj) {
  var index = dvt.ArrayUtils.getIndex(this._tabStopsNodeArray, obj);
  if (index >= 0) {
    this._tabStopsNodeArray.splice(index, 1);
  }
};


/**
 * Resets tab stops
 */
dvt.SearchPanel.prototype.resetTabStops = function() {
  var focusElement = this.getCurrentTabStop();
  if (focusElement && focusElement == this) {
    this.blurEditTextField();
  }
  else if (focusElement && focusElement.hideKeyboardFocusEffect)
    focusElement.hideKeyboardFocusEffect();
  this._curTabStopIdx = -1;
};


/**
 * Gets next navigable element
 * @param {dvt.KeyboardEvent} event keyboard event
 * @return {dvt.SearchPanel|dvt.AfComponent|DvtSearchResultsPanel}  dvt.SearchPanel, af:component from additional content or search results panel
 */
dvt.SearchPanel.prototype.getNextNavigable = function(event) {
  var navigable = null;
  var currTabStop = this.getCurrentTabStop();
  if (currTabStop && currTabStop instanceof dvt.SearchPanel) {
    this.blurEditTextField();
  }
  else if (currTabStop && currTabStop.hideKeyboardFocusEffect) {
    currTabStop.hideKeyboardFocusEffect();
  }

  if (event.keyCode == dvt.KeyboardEvent.TAB && !event.shiftKey) { // navigate forward
    navigable = this.getNextTabStop();
  }
  else if (event.keyCode == dvt.KeyboardEvent.TAB) { // navigate back
    navigable = this.getPrevTabStop();
  }
  else
    navigable = null;
  return navigable;
};



/////////////////////////////////////////////////
//start INNER CLASS

var DvtSearchResultsNodeData = function() {
  this._id = null;
  this._elAttributes = [];
};

dvt.Obj.createSubclass(DvtSearchResultsNodeData, dvt.Obj, 'DvtSearchResultsNodeData');

DvtSearchResultsNodeData.prototype.setId = function(id) {
  this._id = id;
};

DvtSearchResultsNodeData.prototype.getId = function() {
  return this._id;
};

DvtSearchResultsNodeData.prototype.setElAttributes = function(attrs) {
  this._elAttributes = attrs;
};

DvtSearchResultsNodeData.prototype.getElAttributes = function() {
  return this._elAttributes;
};

//end INNER CLASS
/////////////////////////////////////////////////
// Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
/**
 * @constructor
 */
var DvtSearchPanelEventManager = function(context, callback, callbackObj) {
  this.Init(context, callback, callbackObj);
};

dvt.Obj.createSubclass(DvtSearchPanelEventManager, dvt.EventManager, 'DvtSearchPanelEventManager');

/**
 * @protected
 * Initializer for this object
 * @param {dvt.Context} context The platform specific context object
 * @param {function} callback A function that responds to component events.
 * @param {dvt.AdfDiagram} callbackObj The dvt.AdfDiagram object that the callback function is defined on.
 */
DvtSearchPanelEventManager.prototype.Init = function(context, callback, callbackObj) {
  DvtSearchPanelEventManager.superclass.Init.call(this, context, callback, callbackObj);
  this._diagram = callbackObj;
};


/**
 * Mouse down event handler.
 * @protected
 * @param {dvt.MouseEvent} event Mouse event
 */
DvtSearchPanelEventManager.prototype.OnMouseDown = function(event) {
  var obj = this.GetLogicalObject(this.GetCurrentTargetForEvent(event));
  DvtSearchPanelEventManager.superclass.OnMouseDown.call(this, event);

  // Done if there is no object
  if (!obj) {
    return;
  }

  if (obj.HandleMouseDown) {
    obj.HandleMouseDown(event);
  }
  //Enable the search keyboard handler when clicked anywhere inside search component
  //On Mouse down on search panel, the subsequent keyboard event should be directed to search panel
  //This is a workaround to deal with component keyboard handlers at the same level
  //TO DO: Bug 18792535 Need to replace the keyboard navigation workaround
  this._diagram.enableSearchKeyboardHandler();
  this._diagram.getSearchPanel().setCurrentTabStop(obj);

  //  event.stopPropagation();
};


/**
 * Click event handler.
 * @protected
 */
DvtSearchPanelEventManager.prototype.OnClick = function(event) {
  var obj = this.GetLogicalObject(this.GetCurrentTargetForEvent(event));
  DvtSearchPanelEventManager.superclass.OnClick.call(this, event);

  // Done if there is no object
  if (!obj) {
    return;
  }

  if (obj.HandleClick) {
    obj.HandleClick(event);
  }
  event.stopPropagation();
};


/**
 * Double click event handler.
 * @protected
 */
DvtSearchPanelEventManager.prototype.OnDblClickInternal = function(event) {
  var obj = this.GetLogicalObject(this.GetCurrentTargetForEvent(event));

  // Done if there is no object
  if (!obj) {
    return;
  }

  if (obj.isDoubleClickable && obj.isDoubleClickable() && obj.HandleDblClick) {
    obj.HandleDblClick(event);
  }
  event.stopPropagation();
};


/**
 * Roll Over event handler
 * @protected
 */
DvtSearchPanelEventManager.prototype.OnRollOver = function(event) {
  DvtSearchPanelEventManager.superclass.OnRollOver.call(this, event);
  var obj = this.GetLogicalObject(this.GetCurrentTargetForEvent(event));

  // Return if no object is found
  if (!obj) {
    return;
  }

  if (obj.HandleRollOver) {
    obj.HandleRollOver(event);
  }
};


/**
 * Roll Out event handler
 * @protected
 */
DvtSearchPanelEventManager.prototype.OnRollOut = function(event) {
  DvtSearchPanelEventManager.superclass.OnRollOut.call(this, event);
  var obj = this.GetLogicalObject(this.GetCurrentTargetForEvent(event));

  // Return if no object is found
  if (!obj) {
    return;
  }

  if (obj.HandleRollOut) {
    obj.HandleRollOut(event);
  }
};


/**
 * Touch Click event handler.
 * @protected
 */
DvtSearchPanelEventManager.prototype.OnComponentTouchClick = function(event) {
  var obj = this.GetLogicalObject(this.GetCurrentTargetForEvent(event));
  DvtSearchPanelEventManager.superclass.OnComponentTouchClick.call(this, event);

  // Done if there is no object
  if (!obj) {
    return;
  }

  if (obj.HandleClick) {
    obj.HandleClick(event);
  }
  event.stopPropagation();
};


/**
 * @override
 */
DvtSearchPanelEventManager.prototype.HandleTouchDblClickInternal = function(event) {
  var obj = this.GetLogicalObject(this.GetCurrentTargetForEvent(event));

  // Done if there is no object
  if (!obj) {
    return;
  }

  if (obj.isDoubleClickable && obj.isDoubleClickable() && obj.HandleDblClick) {
    obj.HandleDblClick(event);
  }
  event.stopPropagation();
};


//Bug 16901294: enable text field to get focus
/**
 * @protected
 * @override
 */
DvtSearchPanelEventManager.prototype.ShowKeyboardFocusByDefault = function(event) {
  return true;
};

/**
 * @override
 */
DvtSearchPanelEventManager.prototype.setFocus = function(navigable)
{
  //setFocus method in base class checks for keyboard handler
  //since we don't have keyboard handler for searchPanelEventManager, overriding setFocus method
  //TO DO: Bug 18792535 Need to replace the keyboard navigation workaround
  this._focusedObj = navigable;
};

/**
 * @override
 */
DvtSearchPanelEventManager.prototype.getFocus = function()
{
  //getFocus method in base class checks for keyboard handler
  //since we don't have keyboard handler for searchPanelEventManager, overriding getFocus method
  //TO DO: Bug 18792535 Need to replace the keyboard navigation workaround
  return this._focusedObj;
};

// Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
/**
 * @constructor
 */
var DvtSearchResultsPanel = function(context, titleResource, emptyText, callback, callbackObj) {
  this.Init(context, titleResource, emptyText, callback, callbackObj);
};

dvt.Obj.createSubclass(DvtSearchResultsPanel, dvt.Container, 'DvtSearchResultsPanel');

DvtSearchResultsPanel._BORDER_COLOR = '#C1D0D7';

DvtSearchResultsPanel._TEXT_COLOR = '#333333';
DvtSearchResultsPanel._TEXT_FONT = 'Arial';
DvtSearchResultsPanel._TEXT_SIZE = 11;
DvtSearchResultsPanel._TEXT_WEIGHT = 'normal';
DvtSearchResultsPanel._TITLE_BAR_HEIGHT = 19;//25;

DvtSearchResultsPanel._ANIM_DURATION = .25; //in seconds

DvtSearchResultsPanel._CLOSE_BUTTON_WIDTH = 11;
DvtSearchResultsPanel._CLOSE_BUTTON_HEIGHT = 11;

DvtSearchResultsPanel._PGL_BASE_STYLE = 'background-color:transparent;border-width:0px;fill-type:solid;padding:0px;';


/**
 * @protected
 * Helper method called by constructor
 *
 * @param {dvt.Context} context Context object
 * @param {string} titleResource Search results title
 * @param {emptyText} emptyText Text to display when search results are empty
 * @param {function} callback The function that should be called to dispatch events
 * @param {dvt.SearchPanel} callbackObj The dvt.SearchPanel object on which the callback function is defined
 */
DvtSearchResultsPanel.prototype.Init = function(context, titleResource, emptyText, callback, callbackObj) {
  DvtSearchResultsPanel.superclass.Init.call(this, context);

  this._callback = callback;
  this._callbackObj = callbackObj;
  this._eventManager = this._callbackObj.getEventManager();

  this._styleMap = callbackObj.getStyleMap();

  if (titleResource) {
    this._titleResource = titleResource;
  }
  else {
    this._titleResource = 'Search Results [{0}]';
  }
  if (emptyText) {
    this._emptyText = emptyText;
  }
  else {
    this._emptyText = 'No results to display.';
  }
  this._actualItemCount = 0;
  this._total = -1;
  this._items;
  this._maxWidth = Number.MAX_VALUE;//-1;
  this._maxHeight = Number.MAX_VALUE;//-1;
  this._minWidth = -1;
  this._origWidth = 0;
  this._origHeight = 0;
  this._popupMenu;
  this._popupMenuMinWidth;
  this._scrollableContainer;
  this._contentPGL;
  this._titleTextField;
  this._bHScroll = false;
  this._bVScroll = false;
  this._contentWidth;
  this._closeTooltip = 'Close';
  this._closeButton;

  this._firstVisIndex = -1;
  this._lastVisIndex = -1;
  this._selectedIndex = -1;

  this._skinName = null;

  this._imageURIs = null;
  this._textColor = DvtSearchResultsPanel._TEXT_COLOR;
  this._textFont = DvtSearchResultsPanel._TEXT_FONT;
  this._textSize = DvtSearchResultsPanel._TEXT_SIZE;
  this._textWeight = DvtSearchResultsPanel._TEXT_WEIGHT;
  this._titleBarHeight = DvtSearchResultsPanel._TITLE_BAR_HEIGHT;
  this._closeButtonWidth = DvtSearchResultsPanel._CLOSE_BUTTON_WIDTH;
  this._closeButtonHeight = DvtSearchResultsPanel._CLOSE_BUTTON_HEIGHT;

  this._borderColor = dvt.StyleUtils.getStyle(this._styleMap, dvt.CSSStyle.BORDER_COLOR, DvtSearchResultsPanel._BORDER_COLOR);
  this._bgColor = dvt.StyleUtils.getStyle(this._styleMap, dvt.CSSStyle.BACKGROUND_COLOR, null);

  //Fetch event status flag
  this._bFetchFired = false;
};

DvtSearchResultsPanel.prototype.renderComponent = function(parent, items, total, maxWidth, maxHeight, minWidth, firstVisIndex, lastVisIndex, selectedIndex, imageURIs) {
  if (total || total == 0) {
    this._total = total;
  }
  else {
    this._total = -1;
  }

  if (maxWidth || maxWidth == 0) {
    this._maxWidth = maxWidth;
  }
  else {
    this._maxWidth = Number.MAX_VALUE;//-1;
  }
  if (maxHeight || maxHeight == 0) {
    this._maxHeight = maxHeight;
  }
  else {
    this._maxHeight = Number.MAX_VALUE;//-1;
  }
  if (minWidth || minWidth == 0) {
    //subtract 3 from minWidth to account for borders, etc.
    this._minWidth = minWidth - 3;
  }
  else {
    this._minWidth = -1;
  }

  //initialization parameters for restoring scroll state of results after a refresh
  if (firstVisIndex || firstVisIndex == 0) {
    this._firstVisIndex = firstVisIndex;
  }
  else {
    this._firstVisIndex = -1;
  }
  if (lastVisIndex || lastVisIndex == 0) {
    this._lastVisIndex = lastVisIndex;
  }
  else {
    this._lastVisIndex = -1;
  }
  if (selectedIndex || selectedIndex == 0) {
    this._selectedIndex = selectedIndex;
  }
  else {
    this._selectedIndex = -1;
  }

  //temporarily add the results panel to the display list for rendering
  if (parent) {
    parent.addChild(this);
  }

  this._imageURIs = imageURIs;
  this.setItems(items);

  if (parent) {
    parent.removeChild(this);
  }
};

DvtSearchResultsPanel.prototype.setCloseTooltip = function(s) {
  this._closeTooltip = s;
};

/**
 * Show search results panel
 * @param {boolean} bImmediate  true to display the results immediately without animation
 * @return {dvt.Animator}  animator object if animation is enabled
 */
DvtSearchResultsPanel.prototype.show = function(bImmediate) {
  //Don't animate for non-browser environment
  if (this.IsSkinAlta() || !dvt.Agent.isEnvironmentBrowser()) {
    //Bug 18791998 - hv search results is not scrollable on ie10
    //Content clip path animation of panel drawer and scaleY animation of search results panel doesn't work together on IE10
    //Don't animate Search Results for Alta Skin, as the panel drawer already animates while displaying search results
    bImmediate = true;
  }

  if (!bImmediate) {
    this.setAlpha(0);
    this.setScaleY(0.001);
  } else {
    this.setAlpha(1);
    this.setScaleY(1);
  }
  this.setVisible(true);

  var t;
  if (!bImmediate) {
    t = new dvt.Animator(this.getCtx(), DvtSearchResultsPanel._ANIM_DURATION);
    t.addProp(dvt.Animator.TYPE_NUMBER, this, this.getAlpha, this.setAlpha, 1);
    t.addProp(dvt.Animator.TYPE_NUMBER, this, this.getScaleY, this.setScaleY, 1);
  }

  //reset fetch event status flag
  this._bFetchFired = false;

  return t;
};

/**
 * Hide search results panel
 * @return {dvt.Animator}  animator object
 */
DvtSearchResultsPanel.prototype.hide = function() {
  //Don't animate Search Results for Alta Skin, as the panel drawer already animates while hiding search results
  if (this.IsSkinAlta()) {
    this.setAlpha(0);
    this.setScaleY(0.001);
    this.MakeHidden();
    return null;
  }

  var t = new dvt.Animator(this.getCtx(), DvtSearchResultsPanel._ANIM_DURATION);
  t.addProp(dvt.Animator.TYPE_NUMBER, this, this.getAlpha, this.setAlpha, 0);
  t.addProp(dvt.Animator.TYPE_NUMBER, this, this.getScaleY, this.setScaleY, 0.001);
  dvt.Playable.appendOnEnd(t, this.MakeHidden, this);
  return t;
};

DvtSearchResultsPanel.prototype.MakeHidden = function() {
  this.setVisible(false);
};

DvtSearchResultsPanel.prototype.setItems = function(items) {
  this._actualItemCount = 0;
  if (items) {
    this._actualItemCount = items.length;
  }

  this.SetItemsHelper(items);

  if (this._items) {
    this.RenderSelf();
  }
};

DvtSearchResultsPanel.prototype.SetItemsHelper = function(items) {
  this._items = items;

  //add a fake item to the end of the list to allow for virtualization if
  //the row count is unknown
  //NOTE: this._total needs to be set before items in order for this to work
  if (this._items && this._total < 0) {
    var dims = dvt.DisplayableUtils.getDimensionsForced(this.getCtx(), this._items[0]);
    var s = new dvt.Rect(this.getCtx(), 0, 0, dims.w, dims.h);
    s.setInvisibleFill();
    this._items.push(s);
  }
};

DvtSearchResultsPanel.prototype.getItems = function() {
  //if unknown total row count, remove the fake fetch row from the end
  //of the array returned
  if (this._total < 0 && this._items) {
    return this._items.slice(0, this._items.length - 2);
  }

  return this._items;
};

DvtSearchResultsPanel.prototype.getWidth = function() {
  return this._origWidth;
};

DvtSearchResultsPanel.prototype.getHeight = function() {
  return this._origHeight;
};

DvtSearchResultsPanel.prototype.getSkinName = function() {
  return this._skinName;
};


/**
 * Set Skin Name
 * @public
 * @param {string} name  skin name
 */
DvtSearchResultsPanel.prototype.setSkinName = function(name) {
  this._skinName = name;

  if (this.IsSkinAlta()) {
    this._textColor = '#737373';
    this._textFont = '\"Helvetica Neue\", Helvetica, Arial, sans-serif';
    this._textWeight = 'bold';
    this._textSize = 12;
    this._titleBarHeight = 32;
    this._closeButtonWidth = 13;
    this._closeButtonHeight = 13;
  }
};

DvtSearchResultsPanel.prototype.IsSkinSkyros = function() {
  return (this._skinName == dvt.CSSStyle.SKIN_SKYROS);
};

DvtSearchResultsPanel.prototype.IsSkinAlta = function() {
  return (this._skinName == dvt.CSSStyle.SKIN_ALTA);
};

/**
 * Get Search Results title bar text
 * @return {String} title text
 */
DvtSearchResultsPanel.prototype.GetTitleText = function() {
  var s = '';
  if (this._titleResource) {
    var itemCount = '0';
    if (this._total < 0) {
      itemCount = String(this._actualItemCount) + '+';
    }
    else if (this._items) {
      itemCount = String(this._actualItemCount);
    }
    s = dvt.Bundle.format(this._titleResource, [itemCount]);
  }
  return s;
};

DvtSearchResultsPanel.prototype.GetEmptyText = function() {
  return this._emptyText;
};

/**
 * Get close button
 *
 * @return {dvt.Button} close button;
 */
DvtSearchResultsPanel.prototype.getCloseButton = function() {
  return this._closeButton;
};

/**
 * @protected
 * Render Search results panel
 */
DvtSearchResultsPanel.prototype.RenderSelf = function() {
  var titleBar = this.CreateTitleBar(this._minWidth);
  this.addChild(titleBar);
  var titleBarDims = titleBar.getDimensions();
  var itemList = null;
  //create list of items, or show emptyText if list is empty
  if (this._items && this._items.length > 0) {
    var minWidth = titleBarDims.w;
    //For Alta Skin, the minimum width is reduced by the alta padding and scrollbar width
    if (this.IsSkinAlta()) {
      minWidth = minWidth - dvt.ItemList._ALTA_PADDING;

      //Bug 18780427 - hv search input textbox doesn't resize when panel resizes
      //Search results panel shouldn't resize based on content.  The scrollbars should appear if the content larger to fit.
      //If the height of all the result items is more than maximum allowed height of scrollable container,
      //then vertical scroll bar will be enabled by default.
      //If vertical scroll bar is enabled, reduce the content minimum width by the scrollbar width to align the results panel
      //Note, this only sets the content minimum width to align the search results panel to the search panel
      //If the content width is more than minimum width, horizontal scrollbar will appear by default.
      var maxItemListHeight = this.CalcMaxItemListHeight();
      var iDims = dvt.DisplayableUtils.getDimensionsForced(this.getCtx(), this._items[0]);
      if ((iDims.h + (2 * dvt.ItemList._ALTA_PADDING)) * (this._items.length) > maxItemListHeight)
        minWidth -= DvtScrollableContainer.DEFAULT_ALTA_SCROLLBAR_WIDTH;
    }
    itemList = this.CreateItemList(minWidth);
  }
  else {
    itemList = this.CreateEmptyItemList(titleBarDims.w);
  }

  var itemListDims = itemList.getDimensions();

  //Bug 16893124 - HTML5 HV: EMPTY SEARCH RESULTS NOT SIZED CORRECTLY
  //get maximum width of searchBox, titleBar and itemList
  var itemListWidth = Math.max(itemListDims.w, this._minWidth);

  //  if (itemList instanceof dvt.AfComponent) {
  //    itemListWidth = itemList.getCalcWidth(itemListWidth);
  //  }
  if (titleBarDims.w != itemListWidth) {
    //Bug 15839275: need to remove the old title bar before creating a new one,
    //and then add the new one to the results panel after creating it
    var titleBarIndex = this.getChildIndex(titleBar);
    this.removeChild(titleBar);
    if (titleBar.destroy) {
      titleBar.destroy();
    }

    titleBar = this.CreateTitleBar(itemListWidth);
    this.addChildAt(titleBar, titleBarIndex);
  }

  var separator = this.MakeSeparator(itemListWidth, 1);
  separator.setTranslateY(titleBarDims.h);
  this.addChild(separator);
  var separatorDims = separator.getDimensions();
  itemList.setTranslateY(separator.getTranslateY() + separatorDims.h);

  //render a white background behind the whole panel
  //dims won't be correct because they include size clipped out by
  //scrollable container
  var dims = this.getDimensions();//dvt.DisplayableUtils.getDimensionsForced(this.getCtx(), this);
  //  if (itemList instanceof dvt.AfComponent) {
  //    hh -= (itemListDims.h - itemList.getCalcHeight(itemListDims.h));
  //  }
  //update dims with correct values
  var bgRect = new dvt.Rect(this.getCtx(), 0, 0, dims.w, dims.h);
  bgRect.setSolidFill('#ffffff');
  this.SetPanelLineStyle(bgRect);
  this.addChildAt(bgRect, 0);

  //figure out visible size of panel for animating showing/hiding it
  this._origWidth = itemListWidth;
  if (this._maxWidth > 0 && this._maxWidth < itemListWidth)
    this._origWidth = this._maxWidth;
  this._origHeight = dims.h;
  if (this._maxHeight > 0 && this._maxHeight < dims.h)
    this._origHeight = this._maxHeight;

  //initially hide the panel until show() is called
  this.setAlpha(0);
  this.setVisible(false);

  if (this._popupMenu) {
    var focusIdx = this._popupMenu.getFocusIndex();
    //set focus index to enable scroll
    if (focusIdx > -1) {
      //reset the focus index, to enable scrolling to selected item
      this._popupMenu.setFocusIndex(focusIdx, true);
      this.ScrollFocusItemIntoView();
    }
  }

  //clear out initial scroll params after rendering
  this._firstVisIndex = -1;
  this._lastVisIndex = -1;
  this._selectedIndex = -1;
};


/**
 * @protected
 * Create Search Results panel title bar
 * @param {number} ww  width of the title bar
 * @return {dvt.Rect}  Title bar
 */
DvtSearchResultsPanel.prototype.CreateTitleBar = function(ww) {
  var titleBar = new dvt.Rect(this.getCtx(), 0, 0, 1, 1);

  var padding = 3;
  var closeButtonPadding = 5;
  if (this.IsSkinAlta()) {
    padding = dvt.SearchPanel._ALTA_CONTENT_PADDING;
    closeButtonPadding = dvt.SearchPanel._ALTA_CONTENT_PADDING;
    titleBar.setFill(null);
  }
  this._closeButton = this.CreateCloseButton();

  this._titleTextField = this.CreateTitleTextField(this.GetTitleText());
  var titleTFDims = this._titleTextField.measureDimensions();

  //Bug 16893124 - HTML5 HV: EMPTY SEARCH RESULTS NOT SIZED CORRECTLY
  if (ww < 0 || titleTFDims.w > ww) {
    ww = padding + titleTFDims.w + padding + this._closeButtonWidth + closeButtonPadding;
  }

  if (!dvt.Agent.isRightToLeft(this.getCtx())) {
    this._titleTextField.setTranslateX(padding);
  }
  //BiDi: right-align the title text
  else {
    this._titleTextField.setTranslateX(ww - padding - titleTFDims.w);
  }
  this._titleTextField.setTranslateY((this._titleBarHeight - titleTFDims.h) / 2);
  titleBar.addChild(this._titleTextField);

  if (!dvt.Agent.isRightToLeft(this.getCtx())) {
    this._closeButton.setTranslateX(ww - closeButtonPadding - this._closeButtonWidth);
  }
  //BiDi: left-align the close button
  else {
    this._closeButton.setTranslateX(closeButtonPadding);
  }
  this._closeButton.setTranslateY((this._titleBarHeight - this._closeButtonHeight) / 2);
  titleBar.addChild(this._closeButton);

  this.RenderTitleBarBackground(titleBar, 0, 0, ww, this._titleBarHeight);

  //Bug 16893124 - HTML5 HV: EMPTY SEARCH RESULTS NOT SIZED CORRECTLY
  this._titleBarWidth = ww;
  return titleBar;
};

/**
 * @protected
 * Handle click of close button in the search results panel
 * @param {dvt.MouseEvent} event  mouse event
 */
DvtSearchResultsPanel.prototype.HandleCloseClick = function(event) {
  var dvtT = this.hide();
  if (dvtT) {
    dvt.Playable.appendOnEnd(dvtT, this.FireSearchResultsClearEvent, this);
    dvtT.play();
  } else {
    this.FireSearchResultsClearEvent();
  }
};


/**
 * Create the clear button.
 *
 * @return clear button
 */
DvtSearchResultsPanel.prototype.CreateCloseButton = function() {
  var button = dvt.ButtonLAFUtils.createClearResultsButton(this.getCtx(), this._imageURIs);

  var thisRef = this;
  var proxy = {};
  proxy.HandleClick = function(event) {
    thisRef.HandleCloseClick(event);
  };
  proxy.getTooltip = function() {
    return thisRef._closeTooltip;
  };
  this._eventManager.associate(button, proxy);

  return button;
};


/**
 * Create title text field
 * @protected
 * @param {string} text  Initial text
 * @return {dvt.Text}  text field
 */
DvtSearchResultsPanel.prototype.CreateTitleTextField = function(text) {
  var textField = new dvt.OutputText(this.getCtx(), text);
  textField.setSolidFill(this._textColor);
  textField.setFontSize(this._textSize);
  var cssStyle = new dvt.CSSStyle(dvt.CSSStyle.FONT_FAMILY + ':' + this._textFont + ';' + dvt.CSSStyle.FONT_WEIGHT + ':' + this._textWeight + ';');
  textField.setCSSStyle(cssStyle);
  textField.setMouseEnabled(false);
  return textField;
};


/**
 * @protected
 * Create an empty item list
 * @param {number} minWidth minimum width in pixel
 * @return {DvtScrollableContainer} a scrollable container which contains the empty text
 */
DvtSearchResultsPanel.prototype.CreateEmptyItemList = function(minWidth) {
  var maxItemListWidth = this.CalcMaxItemListWidth();
  var maxItemListHeight = this.CalcMaxItemListHeight();

  this._scrollableContainer = new DvtScrollableContainer(this.getCtx(), minWidth, 0, maxItemListWidth, maxItemListHeight, 0, this.getSkinName());
  this._scrollableContainer.setStyleMap(this._styleMap);
  this.addChild(this._scrollableContainer);
  var paddingX = 4;
  var paddingY = 4;

  var tf = this.CreateTitleTextField(this.GetEmptyText());
  tf.setTranslate(paddingX, paddingY);

  //Bug 16893124 - HTML5 HV: EMPTY SEARCH RESULTS NOT SIZED CORRECTLY
  var tfDims = dvt.DisplayableUtils.getDimensionsForced(this.getCtx(), tf);
  var rect = new dvt.Rect(this.getCtx(), 0, 0, tfDims.w + 2 * paddingX, tfDims.h + 2 * paddingY);
  rect.setFill(null);
  rect.addChild(tf);

  this._scrollableContainer.addChild(rect);

  //BiDi: right-align the empty text
  if (dvt.Agent.isRightToLeft(this.getCtx())) {
    tf.setTranslateX(Math.max(minWidth - paddingX - tfDims.w), 0);
  }

  return this._scrollableContainer;
};

DvtSearchResultsPanel.prototype.CreateItemList = function(minWidth) {
  this._popupMenu = this.CreatePopupMenu(minWidth);

  var maxItemListWidth = this.CalcMaxItemListWidth();
  var maxItemListHeight = this.CalcMaxItemListHeight();

  var itemListDims = dvt.DisplayableUtils.getDimensionsForced(this.getCtx(), this._popupMenu);
  var itemListHeight = itemListDims.h;
  var itemListWidth = itemListDims.w;

  //if restoring scroll state, then make height equal to
  //height of visible items
  if (this._firstVisIndex > -1 && this._lastVisIndex > -1 &&
      this._lastVisIndex >= this._firstVisIndex)
  {
    var accumHeight = 0;
    for (var itemI = this._firstVisIndex; itemI <= this._lastVisIndex; itemI++) {
      var iDims = dvt.DisplayableUtils.getDimensionsForced(this.getCtx(), this._popupMenu.getItem(itemI));
      accumHeight += iDims.h;
    }

    itemListHeight = accumHeight;
  }

  //if total row count is unknown, then there is a fake row inserted at the
  //end of the list of real items, so make sure that that row is initially
  //scrolled out of view if the itemListHeight includes that fake row
  if (this._total < 0) {
    var lastItem = null;
    if (this._popupMenu.getNumItems() > 0) {
      lastItem = this._popupMenu.getItem(this._popupMenu.getNumItems() - 1);
    }
    if (lastItem) {
      //BUG FIX #10219766: need to check whether the height of the
      //real rows is less than the max height, not whether the height of
      //all the rows including the fake row is less than max, because if
      //the fake row is half visible then we still need to scroll it
      //out of view
      var lastItemDims = dvt.DisplayableUtils.getDimensionsForced(this.getCtx(), lastItem);
      if (itemListHeight - lastItemDims.h - 1 <= maxItemListHeight &&
          itemListDims.h == itemListHeight) {
        maxItemListHeight = itemListHeight - lastItemDims.h - 1;
      }
    }
  }

  //  //subtract 1 from content size so that borders do not show
  //  //on right and bottom sides between list and scrollbars
  //  var contentWidth = itemListDims.w;
  //  var contentHeight = itemListDims.h;
  //  if (itemListDims.w > maxItemListWidth) {
  //    itemListHeight += scrollSize;
  //
  //    this._bHScroll = true;
  //  }
  //  //if popupMenu is taller than max height, or if we're restoring
  //  //results and the menu is taller than the previously allocated
  //  //height, check what to do for the scrollbar
  //  if (itemListDims.h > maxItemListHeight ||
  //      itemListDims.h > itemListHeight) {
  //    var rawItem = this._items[0];
  //    //if there is enough space to fit the vertical scrollbar without
  //    //overlapping the content, then just make the content less wide
  //    var rawItemDims = dvt.DisplayableUtils.getDimensionsForced(this.getCtx(), rawItem);
  //    if (itemListWidth - rawItemDims.w > scrollSize) {
  //      contentWidth -= scrollSize;
  //    }
  //    //otherwise, add extra space for the scrollbar to the list
  //    else {
  //      itemListWidth += scrollSize;
  //    }
  //
  //    this._bVScroll = true;
  //  }
  //
  //  itemListWidth = Math.max(itemListWidth, minWidth);

  //  this._contentPGL = this.CreateContentPGL(this._popupMenu, contentWidth, contentHeight);

  //BUG FIX #10219766: calculate ceiling of item list size so that we don't
  //run into off-by-1 mistake that results in scrollbars being shown
  //unnecessarily (CreateContentPGL() called above also uses Math.ceil
  //for determining size of content)
  //var itemListWidthCeil = Math.ceil(itemListWidth);
  //  var itemListHeightCeil = Math.ceil(itemListHeight);

  /*
  //append base style to empty string so that new style string is created, instead
  //of directly referencing base style string
  var strStyle = "" + DvtSearchResultsPanel._PGL_BASE_STYLE;
  strStyle += "width:" + itemListWidthCeil + "px;";
  strStyle += "height:" + itemListHeightCeil + "px;";
  this._scrollableContainer.setInlineStyle(strStyle);*/

  this._scrollableContainer = new DvtScrollableContainer(this.getCtx(), minWidth, 0, maxItemListWidth, maxItemListHeight, 0, this.getSkinName());
  this._scrollableContainer.setStyleMap(this._styleMap);
  //need to attach the scrollable container to the display list before
  //adding children to its contentPane
  this.addChild(this._scrollableContainer);
  this._scrollableContainer.addChild(this._popupMenu);

  //BiDi: if we artificially reduced the content width, then we need to shift
  //the popup for BiDi, because it's actually bigger than the content width
  //NOTE: need to do this AFTER rendering the contentPGL
  //  if (dvt.Agent.isRightToLeft() && itemListDims.w > contentWidth) {
  //    this._popupMenu.setTranslateX(this._popupMenu.getTranslateX() - (itemListDims.w - contentWidth));
  //  }

  var vScrollbar = this._scrollableContainer.hasVerticalScrollbar();
  if (vScrollbar) {
    //if restoring scroll state, scroll to the first visible item
    if (this._firstVisIndex > -1) {
      this._scrollableContainer.setVerticalThumbPosition(this._popupMenu.getItem(this._firstVisIndex).getTranslateY());
    }
    this._scrollableContainer.addEvtListener(dvt.ScrollEvent.SCROLL_EVENT, this.HandleVertScroll, false, this);
  }

  //BiDi: save contentWidth in case we need to add additional items later
  this._contentWidth = this._scrollableContainer.getDimensions().w;

  return this._scrollableContainer;
};


/**
 * Create Af Context
 * @protected
 * @return {dvt.AfContext} dvt.AfContext object
 */
DvtSearchResultsPanel.prototype.CreateAfContext = function() {
  var afContext = new dvt.AfContext(this.getCtx(), this._eventManager);
  afContext.setSkinName(this._skinName);

  //TO DO: need this?
  //afContext.setImageCallback(this, this.onAfImageLoaded);
  return afContext;
};


/**
 * Create the PGL containing the content to be displayed, which will be the
 * popup menu list of results.
 *
 * @param content content to be displayed
 *
 * @return PGL sized to content
 */
DvtSearchResultsPanel.prototype.CreateContentPGL = function(content, ww, hh) {
  /*var contentPGL = new dvt.AfPanelGroupLayout();
  //append base style to empty string so that new style string is created,
  //instead of directly referencing base style string
  var strStyle = "" + DvtSearchResultsPanel._PGL_BASE_STYLE;
  strStyle += "width:" + Math.ceil(ww) + "px;";
  strStyle += "height:" + Math.ceil(hh) + "px;";
  contentPGL.setInlineStyle(strStyle);
  contentPGL.addChild(content);
  return contentPGL;*/

  //"background-color:transparent;border-width:0px;fill-type:solid;padding:0px;";
  var rect = new dvt.Rect(this.getCtx(), 0, 0, Math.ceil(ww), Math.ceil(hh));
  var stroke = new dvt.SolidStroke('#000000', 0, 0);
  rect.setInvisibleFill();
  rect.setStroke(stroke);
  rect.addChild(content);
  return rect;
};


/**
 * @protected
 * Handle a scroll event
 * @param {dvt.ScrollEvent} event scroll event
 */
DvtSearchResultsPanel.prototype.HandleVertScroll = function(event) {
  //Bug 17329022 - HTML5: HORIZONTAL SCROLL TRIGGERS LOADING OF ADDT'L SEARCH RESULTS
  if (event.getY() == null)
    return;

  //Fetch additional results if only partial results are displayed
  this.FetchAdditionalResults();

  //Removed the event on touch devices for performance reasons
  if (!this._bFetchFired && this._popupMenu && !dvt.Agent.isTouchDevice()) {
    var dims = this._scrollableContainer.getDimensions();
    var firstVisIndex = this._popupMenu.getClosestItemIndex(event.getY());
    var lastVisIndex = this._popupMenu.getClosestItemIndex(event.getY() + dims.h - 1);
    var selectedIndex = this._popupMenu.getFocusIndex();
    this.FireSearchResultsScrollEvent(firstVisIndex, lastVisIndex, selectedIndex);
  }
};

/**
 * @protected
 * Fetch additional search results if any
 */
DvtSearchResultsPanel.prototype.FetchAdditionalResults = function() {
  //Bug 18764242 - hv search: additional search results are fetched even when scrolled little
  //_total less than 0 indicates only partial search results are rendered
  if (!this._bFetchFired && this._total < 0 && this._popupMenu) {
    var lastItem = this._popupMenu.getItem(this._popupMenu.getNumItems() - 1);
    if (lastItem) {
      var scrollDelta = this._scrollableContainer.getVerticalContentPosition();
      var dims = this._scrollableContainer.getDimensions();
      //If the scroll position of last item is less than scrollContainer height, that means last item is displayed
      //Fetch additional results when user scrolls to last item
      if (lastItem.getTranslateY() + scrollDelta <= dims.h) {
        this._bFetchFired = true;
        this.FireFetchEvent(this._popupMenu.getNumItems() - 2, dvt.SearchEvent.DIR_NEXT);
      }
    }
  }
};

/**
 * @protected
 * Fire search results scroll event
 * This is to keep track of scrolled and selected result
 * @param {number} index  selected index
 */
DvtSearchResultsPanel.prototype.FireScrollEvent = function(index) {
  //Removed the event on touch devices for performance reasons
  if (!this._bFetchFired && this._popupMenu && !dvt.Agent.isTouchDevice()) {
    var firstVisIndex = 0;
    var lastVisIndex = this._popupMenu.getNumItems() - 1;
    var vScrollbar = this._scrollableContainer.hasVerticalScrollbar();
    if (vScrollbar) {
      var dims = this._scrollableContainer.getDimensions();
      firstVisIndex = this._popupMenu.getClosestItemIndex(this._scrollableContainer.getVerticalThumbPosition());
      lastVisIndex = this._popupMenu.getClosestItemIndex(this._scrollableContainer.getVerticalThumbPosition() + dims.h - 1);
    }
    this.FireSearchResultsScrollEvent(firstVisIndex, lastVisIndex, index);
  }
};

/**
 * Get the focus item index from search results
 * @return {number} focus item index
 */
DvtSearchResultsPanel.prototype.getFocusItemIndex = function() {
  if (this._popupMenu)
    return this._popupMenu.getFocusIndex();
  return -1;
};

/**
 * Set the focus item index in the search results
 * @param {number} index  focus item index
 */
DvtSearchResultsPanel.prototype.setFocusItemIndex = function(index) {
  if (this._popupMenu) {
    this._popupMenu.setFocusIndex(index);
    this.ScrollFocusItemIntoView();
  }
};

DvtSearchResultsPanel.prototype.FireEvent = function(event, source) {
  if (this._callback) {
    this._callback.call(this._callbackObj, event, source);
  }
};

DvtSearchResultsPanel.prototype.FireFetchEvent = function(index, direction) {
  var event = new dvt.SearchEvent(dvt.SearchEvent.SEARCH_RESULTS_FETCH_EVENT);
  event.addParam(dvt.SearchEvent.PARAM_INDEX, index);
  event.addParam(dvt.SearchEvent.PARAM_DIRECTION, direction);
  this.FireEvent(event);
};

DvtSearchResultsPanel.prototype.FireSearchResultsScrollEvent = function(firstVisIndex, lastVisIndex, selectedIndex) {
  var event = new dvt.SearchEvent(dvt.SearchEvent.SEARCH_RESULTS_SCROLL_EVENT);
  event.addParam(dvt.SearchEvent.PARAM_FIRST_VIS_INDEX, firstVisIndex);
  event.addParam(dvt.SearchEvent.PARAM_LAST_VIS_INDEX, lastVisIndex);
  event.addParam(dvt.SearchEvent.PARAM_SELECTED_INDEX, selectedIndex);
  this.FireEvent(event);
};

DvtSearchResultsPanel.prototype.FireSearchResultSelectedEvent = function(index) {
  var event = new dvt.SearchEvent(dvt.SearchEvent.SEARCH_RESULTS_SELECTED_EVENT);
  event.addParam(dvt.SearchEvent.PARAM_INDEX, index);
  this.FireEvent(event);
};

DvtSearchResultsPanel.prototype.FireSearchResultsClearEvent = function() {
  var event = new dvt.SearchEvent(dvt.SearchEvent.SEARCH_RESULTS_CLEAR_EVENT);
  this.FireEvent(event);
};


/**
 * Create the popup menu component to display the results
 * @param {number} minWidth  Minimum width of the menu item
 * @return {dvt.ItemList}  Popup Menu
 */
DvtSearchResultsPanel.prototype.CreatePopupMenu = function(minWidth) {
  //save the min popup menu width to reuse when adding results
  this._popupMenuMinWidth = minWidth;

  var popupMenu = new dvt.ItemList(this.getCtx(), this._items, minWidth, false, this.PopupBackgroundStyle, this, this.HandlePopupMenu, this);
  if (this.IsSkinAlta()) {
    popupMenu.setHoverColor('#ebeced');
    popupMenu.setFocusColor('#CFE8FC');
    popupMenu.setAltaSkin(true);
  }
  popupMenu.renderComponent();
  //if restoring state, set the focused item
  if (this._selectedIndex > -1) {
    //temporarily add item list to compute dimensions of selected item
    this.addChild(popupMenu);
    popupMenu.setFocusIndex(this._selectedIndex);
    this.removeChild(popupMenu);
  }
  popupMenu.setInteractionModel(dvt.ItemList.INT_MODEL_DOUBLE_CLICK);
  return popupMenu;
};


/**
 * Setup line and fill style for background of PopupMenu.
 */
DvtSearchResultsPanel.prototype.PopupBackgroundStyle = function(obj) {
  obj.setInvisibleFill();
};

/**
 * @protected
 * Popup menu handler
 * @param {DvtItemListEvent} event  Item list event fired by dvt.ItemList
 */
DvtSearchResultsPanel.prototype.HandlePopupMenu = function(event) {
  if (event['type'] == DvtItemListEvent.TYPE_SELECTION) {
    this.FireSearchResultSelectedEvent(event.getIndex());
  }
  else if (event['type'] == DvtItemListEvent.TYPE_FOCUS) {
    this.FireScrollEvent(event.getIndex());
  }
};

DvtSearchResultsPanel.prototype.CalcMaxItemListWidth = function() {
  if (this._maxWidth > 0) {
    return this._maxWidth;
  }

  return Number.MAX_VALUE;//-1;
};

DvtSearchResultsPanel.prototype.CalcMaxItemListHeight = function() {
  if (this._maxHeight > 0) {
    return (this._maxHeight - this._titleBarHeight);
  }

  return Number.MAX_VALUE;//-1;
};
/*
DvtSearchResultsPanel.prototype.HandleMouseDown = function(event) {
  //TO DO:
  if (stage != null) {
    stage.focus = this;
  }

  EventUtils.consumeEvent(event);
};*/

/**
 * Handle keyboard event in the search results panel
 * @param {dvt.KeyboardEvent} event  keyboard event
 * @return {boolean}  true if the event is handled
 */
DvtSearchResultsPanel.prototype.handleKeyboardEvent = function(event) {
  var focusIndex;
  var numItems;
  var bConsume = true;

  //TAB = move out of search panel
  if (event.keyCode == dvt.KeyboardEvent.TAB) {
    //highlight the selected index in popup menu
    if (this._popupMenu) {
      //TAB = focus on the search result
      var selectedIndex = this._popupMenu.getFocusIndex();
      if (!selectedIndex || selectedIndex < 0) {
        selectedIndex = 0;
      }
      var focusable = this._popupMenu.getItemAt(selectedIndex);
      this._eventManager.UpdateActiveElement(focusable, focusable);
      //handle click of the selected item will highlight the item
      event.target = focusable;
      this._popupMenu.HandleClick(event);
    }
    else {
      bConsume = false;
    }
  }
  //ENTER = select focused result item
  else if (event.keyCode == dvt.KeyboardEvent.ENTER) {
    if (this._popupMenu != null) {
      focusIndex = this._popupMenu.getFocusIndex();
      numItems = this._popupMenu.getNumItems();
      if (numItems > 0) {
        if (focusIndex >= 0 && focusIndex < numItems) {
          event.target = this._popupMenu.getItemAt(focusIndex);
          this._popupMenu.HandleDblClick(event);
        }
      }
    }
  //arrow keys = navigate through list of results
  } else if (event.keyCode == dvt.KeyboardEvent.DOWN_ARROW ||
      event.keyCode == dvt.KeyboardEvent.UP_ARROW) {

    if (this._popupMenu != null) {
      var bNext = (event.keyCode == dvt.KeyboardEvent.DOWN_ARROW);
      var nextFocusIndex = -1;
      focusIndex = this._popupMenu.getFocusIndex();
      numItems = this._popupMenu.getNumItems();
      if (numItems > 0) {
        if (bNext) {
          if (focusIndex < 0) { //if nothing has focus, focus the first item
            nextFocusIndex = 0;
          }
          else { //focus the next item
            if (focusIndex < numItems - 1) {
              nextFocusIndex = focusIndex + 1;
            }
          }
        }
        else {
          if (focusIndex < 0) { //if nothing has focus, focus the last item
            //if total row count is unknown, then last item in list is fake item
            //to use for indicating when to fetch additional results, so we don't
            //want to focus that one immediately
            if (this._total < 0)
              nextFocusIndex = numItems - 2;
            else
              nextFocusIndex = numItems - 1;
          }
          else { //focus the previous item
            if (focusIndex > 0) {
              nextFocusIndex = focusIndex - 1;
            }
          }
        }
      }
      if (nextFocusIndex >= 0) {
        this._popupMenu.setFocusIndex(nextFocusIndex);
        this.ScrollFocusItemIntoView();
        var focusable = this._popupMenu.getItemAt(nextFocusIndex);
        this._eventManager.UpdateActiveElement(focusable, focusable);
      }
    }
  }
  return bConsume;
};

/**
 * @protected
 * Scroll the current focus item into view in the list of results.
 */
DvtSearchResultsPanel.prototype.ScrollFocusItemIntoView = function() {
  var focusIndex = this._popupMenu.getFocusIndex();
  var focusItem = this._popupMenu.getItemAt(focusIndex);
  var bVScrollbar = this._scrollableContainer.hasVerticalScrollbar();
  if (focusItem == null || focusIndex < 0 || !bVScrollbar)
    return;

  var fiY = focusItem.getTranslateY();
  var fiH = focusItem.getHeight();
  var viewportSize = this._scrollableContainer.getDimensions();
  var contentOffset = Math.abs(this._scrollableContainer.getVerticalContentPosition());
  var diffY = fiY - contentOffset;
  //scroll up if the top of the item is above the current viewport
  if (diffY < 0)
  {
    if (this.IsSkinAlta())
      diffY -= dvt.ItemList._ALTA_PADDING;
    //to scroll up move the content down
    this._scrollableContainer.setVerticalContentPosition(-(contentOffset + diffY));
  }
  //scroll down if the bottom of the item is below the current viewport
  else if ((diffY + fiH) > viewportSize.h)
  {
    //Verticial content position could be negative
    diffY += fiH - viewportSize.h;
    if (this.IsSkinAlta())
      diffY += dvt.ItemList._ALTA_PADDING;
    //to scroll down move the content up
    this._scrollableContainer.setVerticalContentPosition(-(contentOffset + diffY));
    //Check for additional results if the scrolled item is below current viewport
    this.FetchAdditionalResults();
  }
  //track scroll position
  this.FireScrollEvent(focusIndex);
};

DvtSearchResultsPanel.prototype.DestroyPopupMenu = function(pm) {
  if (pm && pm.destroy) {
    pm.destroy();
  }
};

DvtSearchResultsPanel.prototype.DestroyScrollablePGL = function() {

  //Bug 16893087 - HTML5 HV: SEARCH RESULTS SCROLLBAR TRAPS MOUSE
  if (this._scrollableContainer) {
    var vScrollbar = this._scrollableContainer.hasVerticalScrollbar();
    if (vScrollbar) {
      this._scrollableContainer.removeEvtListener(dvt.ScrollEvent.SCROLL_EVENT, this.HandleVertScroll, false, this);
    }

    if (this._scrollableContainer.destroy)
      this._scrollableContainer.destroy();
    this._scrollableContainer = null;
  }
};


/**
 * Destroys/cleans up the search panel
 */
DvtSearchResultsPanel.prototype.destroy = function() {
  this.DestroyScrollablePGL();

  if (this._contentPGL && this._contentPGL.destroy) {
    this._contentPGL.destroy();
  }
  this._contentPGL = null;

  this.DestroyPopupMenu(this._popupMenu);
  this._popupMenu = null;
};

DvtSearchResultsPanel.LoadIcon = function(bname, t) {
  //TO DO:
  /*  var src:String = ImageLAFUtils.getIconUrl(bname);

  //create a new Loader object to load the image
  var loader:Loader = new Loader();
  loader.mouseEnabled = false;

  t.addChild(loader);

  //create a new URLRequest for the url-encoded src, and try to
  //load the image
  var urlRequest:URLRequest = new URLRequest(src);
  try
  {
    loader.load(urlRequest);
  }
  catch (e:Error)
  {
    //trace("load threw an Error: " + e.message);
  }
  return loader;*/
  return null;
};


/**
 * Render the title bar background.
 */
DvtSearchResultsPanel.prototype.RenderTitleBarBackground = function(obj, xx, yy, ww, hh) {
  this.SetPanelLineAndFill(obj, xx, yy, ww, hh);

  obj.setX(xx);
  obj.setY(yy);
  obj.setWidth(ww);
  obj.setHeight(hh);
};


/**
 * Set up the Graphics object with the line style for the
 * title bar background.
 */
DvtSearchResultsPanel.prototype.SetPanelLineStyle = function(obj) {
  //lineThickness from spec is supposed to be 0, not 1, but
  //using 0 for some reason makes the horizontal scrollbar appear
  //unnecessarily
  if (!this.IsSkinAlta()) {   //Alta skin doesn't have title bar stroke
    obj.setSolidStroke(this._borderColor, .20);
  }
};


/**
 * Set up the Graphics object with the line style for the
 * title bar background.
 */
DvtSearchResultsPanel.prototype.SetPanelFillStyle = function(obj, xx, yy, ww, hh) {

  var fill;
  if (!this.IsSkinSkyros() || !this._bgColor) {
    var startColor = '#C1D0D7';
    var endColor = '#FFFFFF';

    var fillColors = [startColor, endColor];
    var fillAlphas = [1.00, 1.00];
    var fillRatios = [0, 230 / 255];
    var angle = -90;
    //fill_matrix.createGradientBox(ww, hh, (0.5)*Math.PI, xx, yy);
    //{matrixType:"box", x:xx, y:yy, w:ww, h:hh, r:(rot)*Math.PI};
    if (this.IsSkinAlta()) {
      fillColors = ['#e6e9ec', '#edeff1'];
      fillAlphas = [1, 1];
      fillRatios = [0, 1];
      angle = 90;
    }

    fill = new dvt.LinearGradientFill(angle, fillColors, fillAlphas, fillRatios, [xx, yy, ww, hh]);
    obj.setFill(fill);
  }
  else {
    fill = new dvt.SolidFill(this._bgColor);
  }
  obj.setFill(fill);
};


/**
 * Set up the Graphics object with the line and fill style for the
 * title bar background.
 */
DvtSearchResultsPanel.prototype.SetPanelLineAndFill = function(obj, xx, yy, ww, hh) {
  if (!this.IsSkinAlta()) { //Alta Skin doesn't have background
    this.SetPanelLineStyle(obj);
    this.SetPanelFillStyle(obj, xx, yy, ww, hh);
  }
};


/**
 * Create a horizontal separator for between the title bar and results list.
 *
 * @param ww width of separator
 * @param thck thickness of separator
 *
 * @return separator Shape
 */
DvtSearchResultsPanel.prototype.MakeSeparator = function(ww, thck) {
  var shape = new dvt.Container(this.getCtx());

  if (this.IsSkinAlta()) {
    var line1 = new dvt.Path(this.getCtx(), ['M', 0, 0, 'L', ww, 0]);
    line1.setSolidStroke('#D9DFE3', 1, thck);  //AFDvtPanelBorderColor
    shape.addChild(line1);
  } else {
    var line1 = new dvt.Path(this.getCtx(), ['M', 0, 0, 'L', ww, 0]);
    line1.setSolidStroke('#96A8BA', 1, thck);

    var line2 = new dvt.Path(this.getCtx(), ['M', 0, thck, 'L', ww, thck]);
    line2.setSolidStroke('#FFFFFF');

    shape.addChild(line1);
    shape.addChild(line2);
  }

  return shape;
};


/**
 * @override
 */
DvtSearchResultsPanel.prototype.getDimensions = function(targetCoordinateSpace) {

  var dims = this._scrollableContainer.getDimensions();
  if (dims) {
    dims.h += dims.y + this._scrollableContainer.getTranslateY();

    //Bug 16893124 - HTML5 HV: EMPTY SEARCH RESULTS NOT SIZED CORRECTLY
    if (this._titleBarWidth)
      dims.w = Math.max(dims.w, this._titleBarWidth);
  }

  return targetCoordinateSpace && dims ? this.ConvertCoordSpaceRect(dims, targetCoordinateSpace) : dims;
};


/**
 * Returns the dvt.EventManager for this control.
 * @return {dvt.EventManager}
 */
DvtSearchResultsPanel.prototype.getEventManager = function() {
  return this._eventManager;
};

/**
 * Get AF component from the results panel for the passed component id at the specific index
 * @param {Number} index result index
 * @param {String} componentId  component id
 * @return {dvt.AfComponent} Af Component
 */
DvtSearchResultsPanel.prototype.getAfComponent = function(index, componentId) {
  var items = this.getItems();
  if (items && index < items.length) {
    var itemAfComp = dvt.AfComponentFactory.getAfComponent(items[index]);
    return dvt.AfComponentFactory.findAfComponent(itemAfComp, componentId);
  }
  return null;
};

/**
 * Get actual result item count
 * @return {Number} result item count
 */
DvtSearchResultsPanel.prototype.getActualItemCount = function() {
  return this._actualItemCount;
};
})(dvt);
