// Copyright (c) 2011, 2012, Oracle and/or its affiliates. 
// All rights reserved. 

/**
  *  Creates a canvas that supports panning and zooming.
  *  @extends DvtContainer
  *  @class DvtPanZoomCanvas is a platform independent class representing a 
  *  pannable, zoomable canvas.  
  *  <p>
  *  <b>Example:</b><br><br> <code>
  *  var canvas = new DvtPanZoomCanvas(context, 400, 200, 'myCanvas') ;<br>
  *  <br>
  *</code>
  *  @constructor  
  *  @param {DvtContext} context The context object
  *  @param {number} ww The width of the canvas
  *  @param {number} hh The height of the canvas
  *  @param {string} id optional identifier
  */
var DvtPanZoomCanvas = function(context, buttonImages, ww, hh, id)
{
  this.Init(context, buttonImages, ww, hh, id);
};

DvtObj.createSubclass(DvtPanZoomCanvas, DvtContainer, "DvtPanZoomCanvas");

DvtPanZoomCanvas.DEFAULT_PAN_INCREMENT = 15;
DvtPanZoomCanvas.DEFAULT_ZOOM_INCREMENT = .05;

//control panel is not shown
DvtPanZoomCanvas.CONTROL_PANEL_BEHAVIOR_HIDDEN = 0;
//control panel is collapsed
DvtPanZoomCanvas.CONTROL_PANEL_BEHAVIOR_INIT_COLLAPSED = 1;
//control panel is expanded
DvtPanZoomCanvas.CONTROL_PANEL_BEHAVIOR_INIT_EXPANDED = 2;
    
DvtPanZoomCanvas.DEFAULT_DISPLAYED_CONTROLS = 0xffffff;//DvtPanZoomControlPanel.CONTROLS_ALL;


DvtPanZoomCanvas.prototype.Init = function(context, buttonImages, ww, hh, id)
{
  DvtPanZoomCanvas.superclass.Init.call(this, context, id);
  
  this._ww = ww;
  this._hh = hh;
  this._buttonImages = buttonImages;
  //this._panX = 0;
  //this._panY = 0;
  //this._zoom = 1;
  
  this._px = 0;
  this._py = 0;
  this._mx = 0;
  this._my = 0;
  
  this._minPanX = null;
  this._maxPanX = null;
  this._minPanY = null;
  this._maxPanY = null;
  this._minZoom = .1;
  this._maxZoom = 1;
  
  this._panIncrement = DvtPanZoomCanvas.DEFAULT_PAN_INCREMENT;
  this._zoomIncrement = DvtPanZoomCanvas.DEFAULT_ZOOM_INCREMENT;
  this._bTiltPanningEnabled = false;
  this._zoomToFitPadding = 20;  

  this._bPanning = false;
  this._bZooming = false;
  
  this._controlPanel = null;
    
  this._backgroundPane = new DvtRect(this.getContext(), 
                                     0, 0, this._ww, this._hh);
  this.addChild(this._backgroundPane);
  this._backgroundPane.setFill(new DvtSolidFill("white", 0));
  
  this._contentPane = new DvtContainer(this.getContext());
  this.addChild(this._contentPane);
  this._contentPane.setMatrix(new DvtMatrix(this.getContext()));
  
  this._zoomAnimator = null;
  
  this.AddMouseListeners();
  
  this.RenderInternal();
};

/**
  * Get the content pane of the canvas.  The content pane is the
  * object that will be panned and zoomed.  Content should be
  * added as a child of the content pane, not the canvas itself.
  * @returns {DvtContainer} the content pane of the canvas
  */
DvtPanZoomCanvas.prototype.getContentPane = function()
{
  return this._contentPane;
};

/**
  * Get the background pane of the canvas.  The background
  * pane appears behind the content pane, and should be used
  * for styling.
  * @returns {DvtRect} the background pane of the canvas
  */
DvtPanZoomCanvas.prototype.getBackgroundPane = function()
{
  return this._backgroundPane;
};

/**
  * Get the current zoom level. 
  * @returns {number} the current zoom level
  */
DvtPanZoomCanvas.prototype.getZoom = function()
{
  return this._contentPane.getMatrix().getA();
};

/**
  * Get the current horizontal pan position. 
  * @returns {number} the current horizontal pan position
  */
DvtPanZoomCanvas.prototype.getPanX = function()
{
  return this._contentPane.getMatrix().getTx();
};

/**
  * Get the current vertical pan position. 
  * @returns {number} the current vertical pan position
  */
DvtPanZoomCanvas.prototype.getPanY = function()
{
  return this._contentPane.getMatrix().getTy();
};

/**
  * Set the padding to leave around the content when it is zoomed-to-fit.
  * The default value is 20.
  * @param {number} n new zoom-to-fit padding
  */
DvtPanZoomCanvas.prototype.setZoomToFitPadding = function(n)
{
  this._zoomToFitPadding = n;
};

/**
  * Get the padding to leave around the content when it is zoomed-to-fit. 
  * @returns {number} zoom-to-fit padding
  */
DvtPanZoomCanvas.prototype.getZoomToFitPadding = function()
{
  return this._zoomToFitPadding;
};

/**
  * Pan by the given amount. 
  * @param {number} dx horizontal amount to pan by
  * @param {number} dy vertical amount to pan by
  * @param {DvtAnimator} animator optional animator to use to animate the pan
  */
DvtPanZoomCanvas.prototype.panBy = function(dx, dy, animator)
{
  var oldX = this.getPanX();
  var oldY = this.getPanY();
  var newX = this.ConstrainPanX(oldX + dx);
  var newY = this.ConstrainPanY(oldY + dy);
  
  var deltaX = newX - oldX;
  var deltaY = newY - oldY;
  
  this.FirePanEvent(DvtPanEvent.SUBTYPE_PANNING, newX, newY, oldX, oldY, animator);
  
  //this._panX = newX;
  //this._panY = newY;
  
  var mat = null;
  if (animator)
  {
    mat = animator.getDestVal(this._contentPane, this._contentPane.getMatrix);
    if (mat)
    {
      mat = mat.clone();
    }
  }
  if (!mat)
  {
    mat = this._contentPane.getMatrix().clone();
  }
  
  mat.translate(deltaX, deltaY);
  
  var thisRef = this;
  var fireEventFunc = function() {
    thisRef.FirePanEvent(DvtPanEvent.SUBTYPE_PANNED, newX, newY, oldX, oldY, animator);
    };
  
  if (animator)
  {
    animator.addProp(DvtAnimator.TYPE_MATRIX, 
                     this._contentPane, 
                     this._contentPane.getMatrix,
                     this._contentPane.setMatrix,
                     mat);
    DvtPlayable.appendOnEnd(animator, fireEventFunc);
  }
  else
  {
    this._contentPane.setMatrix(mat);
    fireEventFunc();
  }
};

/**
  * Pan to the given position. 
  * @param {number} xx horizontal position to pan to
  * @param {number} yy vertical position to pan to
  * @param {DvtAnimator} animator optional animator to use to animate the pan
  */
DvtPanZoomCanvas.prototype.panTo = function(xx, yy, animator)
{
  var dx = xx - this._GetPanX(animator);
  var dy = yy - this._GetPanY(animator);
  this.panBy(dx, dy, animator);
};

/**
  * Zoom by the given amount. 
  * @param {number} dz amount to zoom by
  * @param {number} xx horizontal center of zoom (if not specified, treated as the horizontal center of the canvas)
  * @param {number} yy vertical center of zoom (if not specified, treated as the vertical center of the canvas)
  * @param {DvtAnimator} animator optional animator to use to animate the zoom
  */
DvtPanZoomCanvas.prototype.zoomBy = function(dz, xx, yy, animator)
{
  if (!xx && xx !== 0)
  {
    xx = this._ww / 2;
  }
  if (!yy && yy !== 0)
  {
    yy = this._hh / 2;
  }
  
  var oldZoom = this.getZoom();
  var newZoom = this.ConstrainZoom(oldZoom * dz);
  
  if (DvtPanZoomCanvas.RoundFloatForCompare(oldZoom) == DvtPanZoomCanvas.RoundFloatForCompare(newZoom))
  {
    return;
  }
  
  var deltaZoom = newZoom / oldZoom;
  
  this.FireZoomEvent(DvtZoomEvent.SUBTYPE_ZOOMING, newZoom, oldZoom, animator, null, xx, yy);
  
  //this._zoom = newZoom;
  
  var mat = null;
  if (animator)
  {
    mat = animator.getDestVal(this._contentPane, this._contentPane.getMatrix);
    if (mat)
    {
      mat = mat.clone();
    }
  }
  if (!mat)
  {
    mat = this._contentPane.getMatrix().clone();
  }
  
  mat.translate(-xx, -yy);
  mat.scale(deltaZoom, deltaZoom);
  mat.translate(xx, yy);
  
  var thisRef = this;
  var fireEventFunc = function() {
    //use current zoom level at time of firing event as new zoom level
    //in event, because if continously scrolling the mouse wheel, each
    //zoom animation gets interrupted by the next one, so each event
    //doesn't actually zoom all the way to the desired scale until the
    //last event
    thisRef.FireZoomEvent(DvtZoomEvent.SUBTYPE_ZOOMED, thisRef.getZoom(), oldZoom, animator, null, xx, yy);
    };
  
  if (animator)
  {
    animator.addProp(DvtAnimator.TYPE_MATRIX, 
                     this._contentPane, 
                     this._contentPane.getMatrix,
                     this._contentPane.setMatrix,
                     mat);
    DvtPlayable.appendOnEnd(animator, fireEventFunc);
  }
  else
  {
    this._contentPane.setMatrix(mat);
    fireEventFunc();
  }
};

/**
  * Zoom to the given scale. 
  * @param {number} zz new scale
  * @param {number} xx horizontal center of zoom (if not specified, treated as the horizontal center of the canvas)
  * @param {number} yy vertical center of zoom (if not specified, treated as the vertical center of the canvas)
  * @param {DvtAnimator} animator optional animator to use to animate the zoom
  */
DvtPanZoomCanvas.prototype.zoomTo = function(zz, xx, yy, animator)
{
  var dz = zz / this._GetZoom(animator);
  this.zoomBy(dz, xx, yy, animator);
};

/**
  * Zoom and pan the content pane to fit the canvas size. 
  * @param {DvtAnimator} animator optional animator to use to animate the zoom-to-fit
  * @param {DvtRectangle} fitBounds optional bounds in content pane coordinate system to zoom-to-fit to
  */
DvtPanZoomCanvas.prototype.zoomToFit = function(animator, fitBounds)
{
  var bounds = fitBounds;
  if (!bounds) {
    bounds = this._contentPane.getDimensions();
  }
  
  var event = this.FireZoomEvent(DvtZoomEvent.SUBTYPE_ZOOM_TO_FIT, null, null, animator, bounds);
  
//  bounds = event.getZoomToFitBounds();
  
  var dzx = (this._ww - 2 * this._zoomToFitPadding) / (bounds.w);
  var dzy = (this._hh - 2 * this._zoomToFitPadding) / (bounds.h);
  var dz = Math.min(dzx, dzy);
  dz = this.ConstrainZoom(dz);
  
  var cxBounds = (bounds.x + bounds.w / 2) * dz;
  var cyBounds = (bounds.y + bounds.h / 2) * dz;
  var dx = (this._ww / 2) - cxBounds;
  var dy = (this._hh / 2) - cyBounds;
  
  this.zoomTo(dz, 0, 0, animator);
  this.panTo(dx, dy, animator);
};

/**
  * Calculate the zoom-to-fit scale. 
  */
DvtPanZoomCanvas.prototype.calcZoomToFitScale = function()
{
  var bounds = this._contentPane.getDimensions();
  
  var event = this.FireZoomEvent(DvtZoomEvent.SUBTYPE_ZOOM_TO_FIT, null, null, null, bounds);
  
  bounds = event.getZoomToFitBounds();
  
  var dzx = (this._ww - 2 * this._zoomToFitPadding) / (bounds.w);
  var dzy = (this._hh - 2 * this._zoomToFitPadding) / (bounds.h);
  var dz = Math.min(dzx, dzy);
  dz = this.ConstrainZoom(dz);
  
  return dz;
};

/**
  * Calculate the zoom-to-fit dimensions. 
  */
DvtPanZoomCanvas.prototype.calcZoomToFitBounds = function()
{
var bounds = this._contentPane.getDimensions();
  
  var event = this.FireZoomEvent(DvtZoomEvent.SUBTYPE_ZOOM_TO_FIT, null, null, null, bounds);
  
  bounds = event.getZoomToFitBounds();
  
  bounds.x -= this._zoomToFitPadding;
  bounds.y -= this._zoomToFitPadding;
  bounds.w += 2 * this._zoomToFitPadding;
  bounds.h += 2 * this._zoomToFitPadding;
  
  return bounds;
};

/**
 * Get the current viewport in the coordinate system of the content pane.
 * @returns  {DvtRectangle}  current viewport
 */
DvtPanZoomCanvas.prototype.getViewport = function()
{
  var topLeftGlobal = this.localToStage(new DvtPoint(0, 0));
  var bottomRightGlobal = this.localToStage(new DvtPoint(this._ww, this._hh));
  var topLeftLocal = this.getContentPane().stageToLocal(topLeftGlobal);
  var bottomRightGlobal = this.getContentPane().stageToLocal(bottomRightGlobal);
  return new DvtRectangle(topLeftLocal.x, topLeftLocal.y, 
                          bottomRightGlobal.x - topLeftLocal.x, 
                          bottomRightGlobal.y - topLeftLocal.y);
};

/**
  * Get the zoom level. 
  * @returns {number} the zoom level
  */
DvtPanZoomCanvas.prototype._GetZoom = function(animator)
{
  if (animator)
  {
    var mat = animator.getDestVal(this._contentPane, this._contentPane.getMatrix);
    if (mat)
    {
      return mat.getA();
    }
  }
  
  return this.getZoom();
};

/**
  * Get the horizontal pan position. 
  * @returns {number} the horizontal pan position
  */
DvtPanZoomCanvas.prototype._GetPanX = function(animator)
{
  if (animator)
  {
    var mat = animator.getDestVal(this._contentPane, this._contentPane.getMatrix);
    if (mat)
    {
      return mat.getTx();
    }
  }
  
  return this.getPanX();
};

/**
  * Get the vertical pan position. 
  * @returns {number} the vertical pan position
  */
DvtPanZoomCanvas.prototype._GetPanY = function(animator)
{
  if (animator)
  {
    var mat = animator.getDestVal(this._contentPane, this._contentPane.getMatrix);
    if (mat)
    {
      return mat.getTy();
    }
  }
  
  return this.getPanY();
};

DvtPanZoomCanvas.prototype.ConstrainPanX = function(xx)
{
  var offsetX = xx;
  if (this._minPanX && offsetX < this._minPanX)
  {
    offsetX = this._minPanX;
  }
  if (this._maxPanX && offsetX > this._maxPanX)
  {
    offsetX = this._maxPanX;
  }
  return offsetX;
};

DvtPanZoomCanvas.prototype.ConstrainPanY = function(yy)
{
  var offsetY = yy;
  if (this._minPanY && offsetY < this._minPanY)
  {
    offsetY = this._minPanY;
  }
  if (this._maxPanY && offsetY > this._maxPanY)
  {
    offsetY = this._maxPanY;
  }
  return offsetY;
};

DvtPanZoomCanvas.prototype.ConstrainZoom = function(zz)
{
  var newZ = zz;
  if (this._minZoom && newZ < this._minZoom)
  {
    newZ = this._minZoom;
  }
  if (this._maxZoom && newZ > this._maxZoom)
  {
    newZ = this._maxZoom;
  }
  return newZ;
};

DvtPanZoomCanvas.RoundFloatForCompare = function(n)
{
  return Math.round(n * 100000);
};

DvtPanZoomCanvas.prototype.RenderInternal = function()
{
  if (this._buttonImages) {
    var controlPanel = this.getControlPanel();
  
    if (!controlPanel)
    {
      controlPanel = new DvtPanZoomControlPanel(this.getContext(), this, this._buttonImages);
      this._controlPanel = controlPanel;
    }
  
    this.addChild(this._controlPanel);
  
    this._controlPanel.setTranslateX(5);
    this._controlPanel.setTranslateY(5);
  }
};

DvtPanZoomCanvas.prototype.setControlPanel = function (controlPanel) {
  if (this._controlPanel)
    this.removeChild(this._controlPanel);
  
  this._controlPanel = controlPanel;
  this.addChild(this._controlPanel);

  this._controlPanel.setTranslateX(5);
  this._controlPanel.setTranslateY(5);
}

DvtPanZoomCanvas.prototype.getControlPanel = function() {
  return this._controlPanel;
}
    
DvtPanZoomCanvas.prototype.AddMouseListeners = function()
{
  this.addEventListener("mousedown", this.HandleMouseDown, false, this);
  if (DvtAgent.getAgent().isGecko())
  {
    //Firefox
    this.addEventListener("DOMMouseScroll", this.HandleMouseWheel, false, this);
  }
  else
  {
    this.addEventListener("mousewheel", this.HandleMouseWheel, false, this);
  }
};

DvtPanZoomCanvas.prototype.HandleMouseDown = function(event)
{
  this.addEventListener("mousemove", this.HandleMouseMove, false, this);
  this.addEventListener("mouseup", this.HandleMouseUp, false, this);

//////////////////////////////////////////////
// Diar - hack to remove filter on map during panning.  
// Need a formal way for doing this / to remove this hack. 
//  if(this.getContentPane().getNumDrawEffects()>0)
//  {
//    this._contentPane._currentDrawEffect = this._contentPane.getDrawEffectAt(0);
//    this._contentPane.removeDrawEffectAt(0);
//  }
//////////////////////////////////////////////
  
  var pos = this.GetRelativeMousePosition(event);
  
  this._mx = pos.x;
  this._my = pos.y;
  this._px = this._mx;
  this._py = this._my;
  this._bDown = true;
};

DvtPanZoomCanvas.prototype.HandleMouseMove = function(event)
{
  if (this._bDown)
  {
    var pos = this.GetRelativeMousePosition(event);
    
    var xx = pos.x;
    var yy = pos.y;
    
    if (event.ctrlKey)
    {
      if (!this._bZooming)
      {
        this.FireZoomEvent(DvtZoomEvent.SUBTYPE_DRAG_ZOOM_BEGIN);
        this._bZooming = true;
        if (this._controlPanel)
        {
          this._controlPanel.mouseDragPanningStarted();
        }
      }
      var dz = 1 + (this._my - yy) / 100;
      this.zoomBy(dz, this._px, this._py);
    }
    else
    {
      if (!this._bPanning)
      {
        this.FirePanEvent(DvtPanEvent.SUBTYPE_DRAG_PAN_BEGIN);
        this._bPanning = true;
        if (this._controlPanel)
        {
          this._controlPanel.mouseDragPanningStarted();
        }
      }
      this.panBy(xx - this._mx, yy - this._my);
    }
    
    this._mx = xx;
    this._my = yy;
  }
};

DvtPanZoomCanvas.prototype.HandleMouseUp = function(event)
{
  this.removeEventListener("mousemove", this.HandleMouseMove, false, this);
  this.removeEventListener("mouseup", this.HandleMouseUp, false, this);

///////////////////////////////////////////////////
// Diar - patch to remove filter on map during panning.  
// Need a formal way for doing this / to remove this hack. 
  if(this._contentPane._currentDrawEffect!=undefined)
    this._contentPane.addDrawEffect(this._contentPane._currentDrawEffect);
///////////////////////////////////////////////////
  
  this._bDown = false;
  
  if (this._bPanning)
  {
    this.FirePanEvent(DvtPanEvent.SUBTYPE_DRAG_PAN_END);
    this._bPanning = false;
    if (this._controlPanel)
    {
      this._controlPanel.mouseDragPanningEnded();
    }
  }
  if (this._bZooming)
  {
    this.FireZoomEvent(DvtZoomEvent.SUBTYPE_DRAG_ZOOM_END);
    this._bZooming = false;
    if (this._controlPanel)
    {
      this._controlPanel.mouseDragPanningEnded();
    }
  }
};

DvtPanZoomCanvas.prototype.HandleMouseWheel = function(event)
{
  var currZoom = this.getZoom();
  //TODO: re-enable animation after fixing how it works in
  //conjunction with overview window
  var animator = null;//new DvtAnimator(this.getContext(), .5);
  //if there is already a zoom animator running, clean it up
  if (this._zoomAnimator)
  {
    var oldZoomAnim = this._zoomAnimator;
    //stop the old animator
    this._zoomAnimator.stop();
    //get the destination zoom level of the old animator so
    //that we can add to it
    currZoom = this._GetZoom(oldZoomAnim);
    this._zoomAnimator = null;
    oldZoomAnim = null;
    
    //change the easing function so that it's fast at the start to
    //blend in more seamlessly with the animation we just stopped
    //partway through
    if (animator)
    {
      animator.setEasing(DvtEasing.cubicOut);
    }
  }
  this._zoomAnimator = animator;
  
  //var dz = 1 + event.wheelDelta / 100;
  //this.zoomBy(dz, event.offsetX, event.offsetY);
  
  var delta = 0;
  if (event.wheelDelta != null)
  {
    delta = event.wheelDelta;
    
    //need to reverse the sign of the delta in Firefox so that mouse 
    //wheel up zooms in and mouse wheel down zooms out
    if (DvtAgent.getAgent().isGecko())
    {
      delta = -delta;
    }
  }
  
  var zz;
  zz = currZoom + this._zoomIncrement * delta;
  //if (event.wheelDelta > 0)
  //{
  //  zz = this.getZoom() + 0.05;
  //}
  //else
  //{
  //  zz = this.getZoom() - 0.05;
  //}
  
  var pos = this.GetRelativeMousePosition(event);
  
  //cancel the mouse wheel event so that the browser doesn't scroll the page
  var docUtils = this.getContext().getDocumentUtils();
  docUtils.cancelDomEvent(event);
  
  this.zoomTo(zz, pos.x, pos.y, this._zoomAnimator);
  
  if (this._zoomAnimator)
  {
    DvtPlayable.appendOnEnd(this._zoomAnimator, this.ClearZoomAnimator, this);
    this._zoomAnimator.play();
  }
};

/**
 * @protected
 */
DvtPanZoomCanvas.prototype.ClearZoomAnimator = function()
{
  this._zoomAnimator = null;
};

/**
 * @protected
 */
DvtPanZoomCanvas.prototype.GetRelativeMousePosition = function(event)
{
  var docUtils = this.getContext().getDocumentUtils();
  var stage = this.getContext().getStage();
  var pos = docUtils.getStageRelativePosition(stage, event.pageX, event.pageY);
  return pos;
};

/**
 * @protected
 */
DvtPanZoomCanvas.prototype.FirePanEvent = function(subType, newX, newY, oldX, oldY, animator)
{
  var panEvent = new DvtPanEvent(subType, newX, newY, oldX, oldY, animator);
  this.FireListener(panEvent);
};

/**
 * @protected
 */
DvtPanZoomCanvas.prototype.FireZoomEvent = function(subType, newZoom, oldZoom, animator, zoomToFitBounds, xx, yy)
{
  var zoomEvent = new DvtZoomEvent(subType, newZoom, oldZoom, animator, zoomToFitBounds, new DvtPoint(xx, yy));
  this.FireListener(zoomEvent);
  return zoomEvent;
};


DvtPanZoomCanvas.prototype.zoomAndCenter = function() {
  this.FireZoomEvent(DvtZoomEvent.SUBTYPE_ZOOM_AND_CENTER);
}
    
/**
 * Get the next incremental, increasing, zoom level.
 * 
 * @param currZoom current zoom level
 * 
 * @return next zoom level
 */
DvtPanZoomCanvas.prototype.getNextZoomLevel = function(currZoom) {
  var zoomLevel = currZoom;

  zoomLevel += this.getZoomIncrement();
  if (zoomLevel > this.getMaxZoom())
    zoomLevel = this.getMaxZoom();

  return zoomLevel;
}
    
/**
 * Get the previous incremental, decreasing, zoom level.
 * 
 * @param currZoom current zoom level
 * 
 * @return previous zoom level
 */
DvtPanZoomCanvas.prototype.getPrevZoomLevel = function(currZoom) {
  var zoomLevel = currZoom;

  zoomLevel -= this.getZoomIncrement();
  if (zoomLevel < this.getMinZoom())
    zoomLevel = this.getMinZoom();

  return zoomLevel;
}
    
/**
 * Set the increment to use for zooming.
 * The increment should be a percentage between 0 and 1. 
 * The default is .05.  
 * 
 * @param n zoom increment
 */
DvtPanZoomCanvas.prototype.setZoomIncrement = function(n) {
  this._zoomIncrement = n;
}
    
/**
 * Get the increment to use for zooming.
 * 
 * @return zoom increment
 */
DvtPanZoomCanvas.prototype.getZoomIncrement = function() {
  return this._zoomIncrement;
}
    
/**
 * Set the increment to use for panning.
 * The increment should be in pixels. 
 * The default is 15.  
 * 
 * @param n pan increment
 */
DvtPanZoomCanvas.prototype.setPanIncrement = function(n) {
  this._panIncrement = n;
}
    
/**
 * Get the increment to use for panning.
 * 
 * @return pan increment
 */
DvtPanZoomCanvas.prototype.getPanIncrement = function() {
  return this._panIncrement;
}
    
/**
 * Set the minimum zoom factor allowed.  
 * The default is .1.
 * 
 * @param n minimum zoom factor
 */
DvtPanZoomCanvas.prototype.setMinZoom = function(n) {
  this._minZoom = n;
}
    
/**
 * Get the minimum zoom factor allowed.
 * 
 * @return minimum zoom factor
 */
DvtPanZoomCanvas.prototype.getMinZoom = function() {
  return this._minZoom;
}
    
/**
 * Set the maximum zoom factor allowed.
 * 
 * @param n maximum zoom factor
 */
DvtPanZoomCanvas.prototype.setMaxZoom = function(n) {
  if (n < 0)
    n = 1;
  this._maxZoom = n;
}

/**
 * Get the maximum zoom factor allowed.
 * 
 * @return maximum zoom factor
 */
DvtPanZoomCanvas.prototype.getMaxZoom = function() {
  return this._maxZoom;
}

/**
 * Set the minimum x coord allowed.  
 * The default is NaN, meaning there is no minimum.
 * 
 * @param n minimum x coord
 */
DvtPanZoomCanvas.prototype.setMinPanX = function(n) {
  this._minPanX = n;
}

/**
 * Get the minimum x coord allowed.  
 * 
 * @return minimum x coord
 */
DvtPanZoomCanvas.prototype.getMinPanX = function() {
  return this._minPanX;
}

/**
 * Set the maximum x coord allowed.  
 * The default is NaN, meaning there is no maximum.
 * 
 * @param n maximum x coord
 */
DvtPanZoomCanvas.prototype.setMaxPanX = function(n) {
  this._maxPanX = n;
}

/**
 * Get the maximum x coord allowed.  
 * 
 * @return maximum x coord
 */
DvtPanZoomCanvas.prototype.getMaxPanX = function() {
  return this._maxPanX;
}

/**
 * Set the minimum y coord allowed.  
 * The default is NaN, meaning there is no minimum.
 * 
 * @param n minimum y coord
 */
DvtPanZoomCanvas.prototype.setMinPanY = function(n) {
  this._minPanY = n;
}

/**
 * Get the minimum y coord allowed.  
 * 
 * @return minimum y coord
 */
DvtPanZoomCanvas.prototype.getMinPanY = function() {
  return this._minPanY;
}

/**
 * Set the maximum y coord allowed.  
 * The default is NaN, meaning there is no maximum.
 * 
 * @param n maximum y coord
 */
DvtPanZoomCanvas.prototype.setMaxPanY = function(n) {
  this._maxPanY = n;
}

/**
 * Get the maximum y coord allowed.  
 * 
 * @return maximum y coord
 */
DvtPanZoomCanvas.prototype.getMaxPanY = function() {
  return this._maxPanY;
}

/**
 * Set whether tilt panning is enabled.
 * 
 * @param b true to enable tilt panning, false to disable it
 */
DvtPanZoomCanvas.prototype.setTiltPanningEnabled = function(b) {
  this._bTiltPanningEnabled = b;
}

/**
 * Determine whether tilt panning is enabled.
 * 
 * @return true if tilt panning is enabled, false if disabled
 */
DvtPanZoomCanvas.prototype.isTiltPanningEnabled = function(b) {
  return this._bTiltPanningEnabled;
}



/**
 * @constructor
 */
var DvtAfContext = function(context, eventManager) {
  this.Init(context, eventManager);
};

DvtAfContext.prototype.Init = function(context, eventManager) {
  this._context = context;
  this._dimInShape = false;          // whether dimensions already cached in shapes
  this._clearCachedObjList();
  this._dirty = 0;
  this.setEventManager(eventManager);
};



/**
 *  Returns a platform dependent implementation of the one and only
 *  tooltip manager.
 *  @type DvtTooltipManager 
 */
DvtAfContext.prototype.getTooltipManager = function () {
  return this.getContext().getTooltipManager();
};

/**
 * get Object context
 */
DvtAfContext.prototype.getContext = function() {
  return this._context;
};


/**
 * get elContext
 */
DvtAfContext.prototype.getELContext = function() {
  return this._elContext;
};

/**
 * set elContext
 */
DvtAfContext.prototype.setELContext = function(elContext) {
  this._elContext = elContext;
};

/**
 * get available width
 */
DvtAfContext.prototype.getAvailableWidth = function() {
  return this._aw;
};

/**
 * set available width
 */
DvtAfContext.prototype.setAvailableWidth = function(value) {
  this._aw = value;
};

/**
 * get available height
 */
DvtAfContext.prototype.getAvailableHeight = function() {
  return this._ah;
};

/**
 * set available height
 */
DvtAfContext.prototype.setAvailableHeight = function(value) {
  this._ah = value;
};


/**
 * get font size
 */
DvtAfContext.prototype.getFontSize = function() {
  if (! this._fs) {
    this._fs = DvtCSSStyle.DEFAULT_FONT_SIZE;
  }
  return this._fs;
};

/**
 * set font size
 */
DvtAfContext.prototype.setFontSize = function(value) {
  this._fs = value;
};


/**
 * get a list of (Id, Shape) pairs
 * those are objects need to getDimensions later
 */
DvtAfContext.prototype.GetObjShapeList = function() {
  if (! this._cachedObjList)
    this._cachedObjList = {};

  return this._cachedObjList;
};


/**
 * get the object count of the list of (Id, Shape) pairs
 */
// DvtAfContext.prototype.GetObjDimCnt = function() {
//   return this._cachedObjCnt;
// };

/**
 * add an entry to the list of (Id, Shape) pairs
 */
DvtAfContext.prototype.AddObjShapePair = function(id, shape) {
  var list = this.GetObjShapeList();
  list[id] = shape;
  this._cachedObjCnt++;
};

DvtAfContext.prototype._getObjDimList = function() {
  if (! this._dimInShape && this._cachedObjCnt) {
    var list = this.GetObjShapeList();
    var shape;
    for (var id in list) {
      shape = list[id];
      // getDimensions and cached in shape
      shape.getCachedDims();
    }
    this._dimInShape = true;
  }
  return this._cachedObjList;
};


/**
 * getDimensions for all shapes in objDimList
 */
DvtAfContext.prototype.GetDimensions = function() {
  this._getObjDimList();

  //Note: destroy the getDimensions object list
  this._clearCachedObjList();
};


/**
 * @params {string} tagName the name of the tag
 * @params {string} styleClass style class
 * @params {string} inlineStyle inlineStyle
 */
DvtAfContext.prototype.GetCSSStyle = function(tagName, styleClass, inlineStyle, isTextStyle) {

  var tagClass = tagName + "|" +  (styleClass || "");
  inlineStyle = inlineStyle || " ";

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

  var sclass = this._tagStyles[tagClass];
  if (! sclass) {
    sclass = {};
    this._tagStyles[tagClass] = sclass;
  }

  if (! sclass[inlineStyle]) {
    var cs = isTextStyle ? DvtCSSStyle.createDefaultTextStyle() : new DvtCSSStyle();

    //TODO: need to get default tag style and style class
    if (styleClass == "AFStretchWidth" || styleClass == "AFAuxiliaryStretchWidth") {
      cs.setStyle(DvtCSSStyle.WIDTH, "100%");
    }
    else {
    }

    // all styles are merged here
    if (inlineStyle !== " ") {
      cs.parseInlineStyle(inlineStyle);
    }
    sclass[inlineStyle] = cs;
  }

  return sclass[inlineStyle];
};


/**
 * Truncate texts for all shapes in objDimList
 */
DvtAfContext.prototype.TruncateTexts = function() {
  if (this._cachedObjList) {

    var list = this.GetObjShapeList();
    var textFields = [];
    for (var id in list) {
      textFields.push(list[id]);
    }
    DvtText.truncateTexts(textFields);

    //Note: destroy the pending truncation object list 
    this._clearCachedObjList();
  }
};


/**
 * clear cached object list
 */
DvtAfContext.prototype._clearCachedObjList = function() {
  this._cachedObjCnt = 0;
  this._cachedObjList = undefined;
};


/**
 * set context callback
 * @param {object} obj instance of the object the callback is defined on
 * @param {function} the function to callback
 */
DvtAfContext.prototype.setContextCallback = function(obj, callback) {
  this._contextCallbackObj = obj;
  this._contextCallback = callback;
};

/**
 * get context callback
 */
DvtAfContext.prototype.getContextCallback = function() {
  return this._contextCallback;
};

/**
 * get context callbackObj
 */
DvtAfContext.prototype.getContextCallbackObj = function() {
  return this._contextCallbackObj;
};

/**
 * remove any components that don't fit in the tree node
 */
DvtAfContext.prototype.setRmIfNotFit = function(flag) {
  this._rmIfNotFit = flag;
};

DvtAfContext.prototype.getRmIfNotFit = function() {
  return this._rmIfNotFit;
};


/**
 * set row index
 */
DvtAfContext.prototype.setRowIndex = function(index) {
  this._rowIndex = index;
};

DvtAfContext.prototype.getRowIndex = function() {
  return this._rowIndex;
};



/**
 * Add dirty when an image is loaded
 * @param {number} amt 1 for increment and -1 for decrement
 */
DvtAfContext.prototype._addDirty = function(amt) {
  this._dirty = Math.max(this._dirty + amt, 0);

  //fire refresh event
  if (amt === -1 && this._refreshCallback) {
    this._refreshCallback(this._dirty);
  }
};

DvtAfContext.prototype._isDirty = function() {
  return this._dirty > 0;
};

DvtAfContext.prototype.setRefreshCallback = function(callback) {
  this._refreshCallback = callback;
};

/**
 * Gets the DvtEventManager that should be used for associating display objects with their logical object counterparts
 * @return {DvtEventManager} eventManager the DvtEventManager instance
 */
DvtAfContext.prototype.getEventManager = function() {
  return this._eventManager;
}

/**
 * Sets the DvtEventManager that should be used for associating display objects with their logical object counterparts
 * @param {DvtEventManager} eventManager the DvtEventManager instance
 */
DvtAfContext.prototype.setEventManager = function(eventManager) {
  this._eventManager = eventManager;
}
/**
 * This is an abstract class, do not use directly!!!
 *
 * Rect for content
 *  _cx  the origin point of the content (clientX)
 *  _cy  (clientY)
 *  _cw  the width of the content (clientWidth)
 *  _ch  (clientHeight)
 *
 * Available size
 *  _aw 
 *  _ah
 *
 * Explicit size
 *  _ew 
 *  _eh
 *
 */
var DvtAfComponent = function() {
  this.Init();
};

/**
 * make DvtAfComponent a subclass of DvtPropMap
 */
DvtObj.createSubclass(DvtAfComponent, DvtPropMap, "DvtAfComponent");


DvtAfComponent.HALIGN_LEFT = "left";
DvtAfComponent.HALIGN_RIGHT = "right";
DvtAfComponent.HALIGN_CENTER = "center";
DvtAfComponent.HALIGN_START = "start";
DvtAfComponent.HALIGN_END = "end";

DvtAfComponent.VALIGN_TOP = "top";
DvtAfComponent.VALIGN_MIDDLE = "middle";
DvtAfComponent.VALIGN_BOTTOM = "bottom";


// Af Component Common Attributes
DvtAfComponent.ATTR_ID = "id";
DvtAfComponent.ATTR_VISIBLE = "visible";
DvtAfComponent.ATTR_RENDERED = "rendered";
DvtAfComponent.ATTR_INLINE_STYLE = "inlineStyle";
DvtAfComponent.ATTR_STYLE_CLASS = "styleClass";
DvtAfComponent.ATTR_SHORT_DESC = "shortDesc";


// For generating unique ID 
DvtAfComponent._uniqueSeed = 0;


// Indicate whether to create a bg shape for the following af components 
// outputText, outputFormatted, image, spacer and separator
DvtAfComponent._CREATE_BG_SHAPE = false;


// possible values for this._clipped
DvtAfComponent._CLIP_PATH = "clipPath"; //this component has a clipPath
DvtAfComponent._REMOVE_LATER = "rmMe"; //removed this component later
DvtAfComponent._CLIP_LATER = "clipMe"; //clip this component later

/**
 * Initializes the instance. 
 */
DvtAfComponent.prototype.Init = function() {
  DvtAfComponent.superclass.Init.call(this);

  // create default style
  this.createDefaultStyles();
};


/**
 * @override
 */
DvtAfComponent.prototype.setProperty = function(propName, val) {
  // turn stretchWidth to width:100%
  if (! DvtAfComponentFactory.CACHE_STYLES) {
  if (propName == DvtAfComponent.ATTR_STYLE_CLASS && 
      (val == "AFStretchWidth" || val == "AFAuxiliaryStretchWidth")) {
    val = DvtCSSStyle.WIDTH + ":100%";
  }
  }
  DvtAfComponent.superclass.setProperty.call(this, propName, val);
};


/*---------------------------------------------------------------------*/
/*   AfComponent common attributes                                     */
/*---------------------------------------------------------------------*/
DvtAfComponent.prototype.getStyleClass = function() {
  return this.getProperty(DvtAfComponent.ATTR_STYLE_CLASS);
};

DvtAfComponent.prototype.setStyleClass = function(styleClass) {
  this.setProperty(DvtAfComponent.ATTR_STYLE_CLASS, styleClass);
};

DvtAfComponent.prototype.getInlineStyle = function() {
  return this.getProperty(DvtAfComponent.ATTR_INLINE_STYLE);
};

DvtAfComponent.prototype.setInlineStyle = function(inlineStyle) {
  this.setProperty(DvtAfComponent.ATTR_INLINE_STYLE, inlineStyle);
};

DvtAfComponent.prototype.isRendered = function() {
  return this.getProperty(DvtAfComponent.ATTR_RENDERED);
};

DvtAfComponent.prototype.setRendered = function(rendered) {
  this.setProperty(DvtAfComponent.ATTR_RENDERED, rendered);
};

DvtAfComponent.prototype.isVisible = function() {
  return this.getProperty(DvtAfComponent.ATTR_VISIBLE);
};

DvtAfComponent.prototype.setVisible = function(visible) {
  this.setProperty(DvtAfComponent.ATTR_VISIBLE, visible);
};

DvtAfComponent.prototype.getShortDesc = function() {
  return this.getProperty(DvtAfComponent.ATTR_SHORT_DESC);
};

DvtAfComponent.prototype.setShortDesc = function(shortDesc) {
  this.setProperty(DvtAfComponent.ATTR_SHORT_DESC, shortDesc);
};




/*---------------------------------------------------------------------*/
/*   stamp                                                             */
/*---------------------------------------------------------------------*/
/**
 * @override
 * Stamp out this template object
 * @param {} elcontext EL binding context
 * @return {DvtAfComponent} a new DvtAfComponent tree
 */
DvtAfComponent.prototype.stamp = function(elcontext) {
  // stamp self
  var selfComp = DvtAfComponent.superclass.stamp.call(this, elcontext);
  selfComp._children = [];

  // stamp children
  var child;
  var childComp;

  for (var i = 0; i < this.getNumChildren(); i++) {
    child = this.getChildAt(i);
    childComp = child.stamp(elcontext);

    if (! child.renderMe || childComp.renderMe()) {

      //if there's a showPopupBehavior tag associated with this component
      //set it as component's property
      //TODO: what if stamp method is not called
      if (childComp instanceof DvtAfShowPopupBehavior) {
        selfComp.addShowPopupBehavior(childComp.createDisplayObj());
      }
      else {
        selfComp.addChild(childComp);
      }
    }
  }

  return selfComp;
};



/** 
 * set the content's origin point
 */
DvtAfComponent.prototype.setContentOrigin = function(cssStyle) {

  // calculate margin, border and padding
  var marginTop = cssStyle.getMarginTop();
  var marginLeft = cssStyle.getMarginLeft();
  var marginRight = cssStyle.getMarginRight();

  var bdTopWidth = cssStyle.getBorderTopWidth();
  var bdLeftWidth = cssStyle.getBorderLeftWidth();
  var bdRightWidth = cssStyle.getBorderRightWidth();

  var paddingTop = cssStyle.getPaddingTop();
  var paddingLeft = cssStyle.getPaddingLeft();
  var paddingRight = cssStyle.getPaddingRight();

  // used to render content
  this._cx = marginLeft + bdLeftWidth + paddingLeft;
  this._cy = marginTop + bdTopWidth + paddingTop;

  //Note: available size may not be specified
//   this._aw = this._aw - this._cx - (marginRight + bdRightWidth + paddingRight);
//   this._ah = this._ah - this._cy - (marginBottom + bdBottomWidth + paddingBottom);

  // used in bidi mode
  this._rightx = marginRight + bdRightWidth + paddingRight;
};

/** 
 * adjust available size not including margin, border and padding
 * if size is specified, set explicit size
 */
DvtAfComponent.prototype.adjustAvailSize = function(cssStyle) {
  // calc width and height
  var num = 0;
  var sw = this._getExplicitWidth(cssStyle);
  if (sw == 0) {
    this._aw = 0;
    this._ew = this._aw;
  }
  else if (sw) {
    num = sw.indexOf("%");
    // size in percent
    if (num > -1) {
      sw = sw.slice(0, num);
      if (this._aw !== Number.MAX_VALUE && sw != null) {
        num = (parseFloat(sw) * this._aw) / 100;
        this._aw = num;
      }
      else {
        num = this._aw;
      }
    }
    // assume pixel for now
    else {
      //round size to the nearet integer
      num = Math.round(parseFloat(sw));
      this._aw = num;
    }

    if (num > this._aw) {
      this._clipped = DvtAfComponent._CLIP_LATER;
    }

    // explicitWidth 
    this._ew = this._aw;
  }

  var sh = this._getExplicitHeight(cssStyle);
  if (sh == 0) {
    this._ah = 0;
    this._eh = this._ah;
  }
  else if (sh) {
    num = sh.indexOf("%");
    // size in percent
    if (num > -1) {
      sh = sh.slice(0, num);
      if (this._ah !== Number.MAX_VALUE && sh != null) {
        num = (parseFloat(sh) * this._ah) / 100;
        this._ah = num;
      }
      else {
        num = this._ah;
      }
    }
    // assume pixel for now
    else {
      //round size to the nearest integer
      num = Math.round(parseFloat(sh));
      this._ah = num;
    }
    if (num > this._ah) {
      this._clipped = DvtAfComponent._CLIP_LATER;
    }

    // explicit height
    this._eh = this._ah;
  }
};


/** 
 * adjust client area
 */
DvtAfComponent.prototype.adjustClientArea = function(cssStyle) {
  this._cw = this._aw;
  this._ch = this._ah;

  if (this._clipped === undefined && this._aw <= 0 || this._ah <= 0) {
    this._clipped = DvtAfComponent._CLIP_LATER;
  }
};


/*---------------------------------------------------------------------*/
/*   render()                                                          */
/*---------------------------------------------------------------------*/
DvtAfComponent.prototype.render = function(context) {

  this._context = context;
  this._render(parseFloat(context.getAvailableWidth()), 
               parseFloat(context.getAvailableHeight()));
};

/**
 * @private
 */
DvtAfComponent.prototype._render = function(availWidth, availHeight) {
  if (! this.renderMe())
    return;

  // get available size
  this._aw = availWidth;
  this._ah = availHeight;

  // perf: create computed style in createDisplayObj
//   if (this.createComputedStyle)
//     this.createComputedStyle();

  var cssStyle = this.getCSSStyle();
  var shape = this.getContentShape();

  // Transfer relevant attributes to shapes
   this._TransferAttributes(shape);

  // calculate margin, border and padding size
  if (  this.setContentOrigin)
    this.setContentOrigin(cssStyle);

  // excluding margin, border and padding size
  if (this.adjustAvailSize)
    this.adjustAvailSize(cssStyle);

  // adjust client area 
  if (this.adjustClientArea)
    this.adjustClientArea(cssStyle);

  //Bug 13486985 - double click on af:image that does not drill-down to the node
  //if shortDesc is specified, show it in a tooltip
  if (shape && this.isTooltipEnabled() && (! this._context.getRmIfNotFit())) {
    this._addMouseOverOutHandler(shape);
  }

  if (this.renderSelfBeforeChildren)
    this.renderSelfBeforeChildren();

  if (this.renderChildren)
    this.renderChildren();

  if (this.renderSelfAfterChildren)
    this.renderSelfAfterChildren();

  if (this.doLayout)
    this.doLayout();

  if (this.renderSelfAfterLayout)
    this.renderSelfAfterLayout();
  
  //Bug 13486985 - double click on af:image that does not drill-down to the node
  //make all af components inside treemap mouse transparent
  if (this._context.getRmIfNotFit()) {
    var bgShape = this.getDisplayObj();
    if (bgShape) {
      bgShape.setMouseEnabled(false);
    }
    var shape = this.getContentShape();
    if (shape && shape !== bgShape) {
      shape.setMouseEnabled(false);
    }
  }
  else {
    //if this AF component has a showPopupBehavior tag, then
    //this AF component needs to be a logical object that the
    //event manager recognizes in order to launch the popup
    if (this.getDisplayObj() && this.getShowPopupBehaviors()) {
      this._context.getEventManager().associate(this.getDisplayObj(), this);
    }
  }

};


/**
 * Note: only tne AfSpacer tag has width and height attributes
 * All other tags specified width and height in cssStyle
 */
DvtAfComponent.prototype._getExplicitWidth = function(cssStyle) {
  var w;
  if (this.getWidth)
    w = this.getWidth();

  if (w === undefined)
    w = cssStyle.getWidth();

  return w;
};

/**
 * Note: only the AfSpacer tag has width and height attributes
 * All other tags specified width and height in cssStyle
 */
DvtAfComponent.prototype._getExplicitHeight = function(cssStyle) {
  var h;
  if (this.getHeight)
    h = this.getHeight();

  if (h === undefined)
    h = cssStyle.getHeight();

  return h;
};

// use the explicit width is set
// use availabel width if the specified width is wider
// use the specified width
DvtAfComponent.prototype.getCalcWidth = function(width) {
  if (this._ew !== undefined && this._ew !== Number.MAX_VALUE) {
    return this._ew;
  }
  if (this._aw < width) {
    return this._aw;
  }
  return width;
};

DvtAfComponent.prototype.getCalcHeight = function(height) {
  if (this._eh !== undefined && this._eh !== Number.MAX_VALUE) {
    return this._eh;
  }
  if (this._ah < height) {
    return this._ah;
  }
  return height;
};

/**
 * Render this object only, before children have been rendered.
 * If not a container, resolve (x, y, w, h)
 */
DvtAfComponent.prototype.renderSelfBeforeChildren = function() {
};


/**
 * Render this object only, after children have been rendered.
 */
DvtAfComponent.prototype.renderSelfAfterChildren = function() {
};

/**
 * @protected
 * 
 * Render this object only, after layout have been calculated.
 */
DvtAfComponent.prototype.renderSelfAfterLayout = function() {
  // set size on bgShape (middle of border width)
  var bgShape = this.getDisplayObj();

  // this.setBX(this._bx);
  // this.setBY(this._by);

  var style = this.getCSSStyle();
  var _tt;

  // Rect for background color or image
  if (bgShape.setX) {
    bgShape.setX(style.getMarginLeft() + style.getBorderLeftWidth() / 2);
    bgShape.setY(style.getMarginTop() + style.getBorderTopWidth() / 2);
  }
  if (bgShape.setWidth) {
    _tt = this._cw + style.getBorderRightWidth() +
              style.getPaddingLeft() + style.getPaddingRight();
    if (_tt !== Number.MAX_VALUE) {
      bgShape.setWidth(_tt);
    }
  }

  if (bgShape.setHeight) {
    _tt = this._ch + style.getBorderBottomWidth() +
              style.getPaddingTop() + style.getPaddingBottom();
    if (_tt !== Number.MAX_VALUE) {
      bgShape.setHeight(_tt);
    }
  }

  if (this._hasBgShape()) {
    this.renderBackground();
  }
};


/**
 * Render the children of this object.
 */
DvtAfComponent.prototype.renderChildren = function() {
  var child;

  for (var i = 0; i < this.getNumChildren(); i++) {
    child = this.getChildAt(i);
    if (! child.renderMe || child.renderMe()) {
      this.renderChild(child);
    }
  }
};

/**
 * Render the children of this object.
 */
DvtAfComponent.prototype.renderChild = function(child) {
  child._render(this._cw, this._ch);
};
    
/**
 * Perform layout of this display object.
 */
DvtAfComponent.prototype.doLayout = function() {
};


/**
 * Add an AfComponent child to the AfComponent tree and 
 * @param {DvtAfComponent} afComp
 * @returns nothing
 */
DvtAfComponent.prototype.addChild = function(afComp) {

//Note: not all children are instance of DvtAfComponet
//  if (afComp instanceof DvtAfComponent) {

  // add the afComp to the afComponent tree
  if (afComp.setParent)
    afComp.setParent(this);

  this._getChildren().push(afComp);

//  }
};

/**
 * Remove an AfComponent child from the AfComponent tree
 * @param {DvtDisplayable} comp
 */
DvtAfComponent.removeChild = function(comp) {
  var parent;

  //Note: not all children are instance of DvtAfComponet
  // remove the afComp from the afComponent tree
  if (comp instanceof DvtAfComponent) {
    parent = comp.getParent();
    var children = parent._getChildren();
    for (var i = 0; i < children.length; i ++) {
      if (children[i] === comp) {
        comp.setParent(null);
        children.splice(i, 1);
        break;
      }
    }
  }

  var displayObj = comp.getDisplayObj();
  var shape = DvtAfComponent._getContentShape(comp);
  if (shape && shape !== displayObj) {
    parent = shape.getParent();
    if (parent) {
      parent.removeChild(shape);
      comp.setContentShape(null);
    }
  }

  if (displayObj) {
    parent = displayObj.getParent();
    if (parent) {
      parent.removeChild(displayObj);
      comp._bgShape = null;
    }
  }
};

DvtAfComponent.prototype._getChildren = function() {
  if (this._children === undefined) {
    this._children = [];
  }
  return this._children;
};

DvtAfComponent.prototype.getNumChildren = function() {
  return (this._children? this._children.length : 0) ;
};

/*
 * Returns the child at the specified zero-relative position, or null if
 * there is no child represented by the position.
 */
DvtAfComponent.prototype.getChildAt = function(idx) {
  var cnt = this.getNumChildren();
  if (idx >= 0 && cnt > idx) {
    return this._getChildren()[idx] ;
  }
  return null;
};

DvtAfComponent.prototype.getParent = function() {
  return this._afCompParent;
};

DvtAfComponent.prototype.setParent = function(parent) {
  this._afCompParent = parent;
};


/*---------------------------------------------------------------------*/
/*   Style                                                             */
/*---------------------------------------------------------------------*/
/** 
 * get the Adf style of this object
 * @return {DvtCSSStyle} the first Adf style
 */
DvtAfComponent.prototype.GetRootStyleClass = function() {
  return this.GetDefaultStyles()[0];
};


/**
 * create an array of Adf styles
 */
DvtAfComponent.prototype.createDefaultStyles = function() {
  // Note: only need to create one instance of _afStyle per class
  var styleList = this.GetDefaultStyles();

  if (styleList === undefined) {
    styleList = DvtStyleUtils.getDefaultCSSStyle(this.getTypeName());

    // if component does not have a default style, create an empty style for it
    if (styleList.length == 0) {
      styleList.push(new DvtCSSStyle());
    }
    this.SetDefaultStyles(styleList);
  }
  // var newList = DvtStyleUtils.cloneStyles(styleList);
  // if (! newList || newList.length == 0) {
  //   newList = [new DvtCSSStyle(null)];
  // }
  // return newList;

};


/** 
 * @private
 */
DvtAfComponent.prototype.createComputedStyle = function() {
  if (! this._computedStyle) {

    // merge styles: defaultStyle, styleClass and inlineStyles
    var def = this.GetRootStyleClass();
    var sc = this.getStyleClass();
    var ins = this.getInlineStyle();

    var cssStyle;
    if (DvtAfComponentFactory.CACHE_STYLES) {
      var isText = (this instanceof DvtAfOutputText || this instanceof DvtAfOutputFormatted);
      //NOTE: may NOT work if style contains EM
      cssStyle = this._context.GetCSSStyle(this.getTagName(), sc, ins, isText);
    }
    else {
      cssStyle = def.clone();
      cssStyle.parseInlineStyle(sc);
      cssStyle.parseInlineStyle(ins);
    }

    // resolve em unit
    this.ResolveEm(cssStyle);

    this._computedStyle = cssStyle;
  }

  return this._computedStyle;
};


/** 
 * @private
 */
DvtAfComponent.prototype.ResolveEm = function(cssStyle) {
  this.resolveEmUnit(cssStyle, this.getParent());
};

/** 
 * @private
 * @param {DvtAfComponent} startComp 
 */
DvtAfComponent.prototype.resolveEmUnit = function(cssStyle, startComp) {
  // get fontSize in the following orders:
  // 1) current fontSize if not in "em"
  // 2) inherited fontSize
  // 3) nfontSize passed in
  // 4) CSSStyle.DEFAULT_FONT_SIZE

  if (cssStyle) {
    var fontSize = cssStyle.getAbsoluteFontSize();

    if (! fontSize && startComp) {
      fontSize = startComp.getInheritedStyleAttr(DvtCSSStyle.FONT_SIZE);
    }
    if (! fontSize) {
      fontSize = this._context.getFontSize();
    }
    cssStyle.resolveEM(fontSize);
  }
};


// rounds the value to the nearest integer
DvtAfComponent.roundOff = function(decVal, defaultVal) {
  if (decVal === undefined || decVal.length == 0)
    return defaultVal;

  return Math.round(parseFloat(decVal));
};


/** 
 * get style 
 */
DvtAfComponent.prototype.getStyle = function(key) {
  if (this._computedStyle)
    return this.getCSSStyle()[key];
  else
    return undefined;
}

DvtAfComponent.prototype.getComputedStyles = function() {
  //TODO: return empty style for now
  return this._computedStyle;
};

// Array of styles
/*
DvtAfComponent.prototype.setComputedStyles = function(computedStyle) {
  this._computedStyle = computedStyle;
}
*/

DvtAfComponent.prototype.getCSSStyle = function() {
  if (this._cssStyle)
    return this._cssStyle;

  var styleList = this.getComputedStyles();
//   if (! styleList) {
//     if (this.createComputedStyle) {
//       this.createComputedStyle();
//       styleList = this._computedStyle;
//     }
//   }

  if (DvtArrayUtils.isArray(styleList)) {
    this._cssStyle = styleList[0];
  }
  else {
    this._cssStyle = (styleList)? styleList : new DvtCSSStyle(null);
  }
  return this._cssStyle;
};

/**
 * Get a style attribute. go up to the parent chain only if attribute is inherited 
 * @private
 */
DvtAfComponent.prototype.getStyleAttr = function(key) {
  var ret = this.getCSSStyle().getStyle(key);

  //if style can be inherited, check parent hierarchy
  if (! ret && DvtCSSStyle.isInheritable(key) && this.getParent()) {
    ret = this.getParent().getInheritedStyleAttr(key);
  }
  return ret;
};

/**
 * Get a style attribute, go up to the parent chain if needed
 * @private
 */
DvtAfComponent.prototype.getInheritedStyleAttr = function(key) {
  var ret = this.getCSSStyle().getStyle(key);

  if (! ret && this.getParent()) {
    ret = this.getParent().getInheritedStyleAttr(key);
  }
  return ret;
};

/**
 * @private
 */
DvtAfComponent.prototype.getInheritedStyle = function(key, style, defaultStyle) {
  var ret;

  if (style) {
    ret = style[key];
  }
  if (! ret) {
    ret = this.getStyleAttr(key);
    if (! ret && defaultStyle) {
      ret = defaultStyle[key];
    }
  }
  return ret;
};
    
/**
 * @private
 * @return a DvtCSSStyle 
 */
DvtAfComponent.prototype.createTextStyles = function(style, defaultStyle) {
  if (style == null) {
      style = this.getCSSStyle().clone();
    if (style == null)
      return null;
  }
  var k;
  var s;

  //Performance: use the same css style dont clone
  // style = style.clone();

  // Default to Tomoha font family
  k = DvtCSSStyle.FONT_FAMILY;
  if (! style[k]) {
    s = this.getInheritedStyle(k, style, defaultStyle);
    style.setStyle(k, (s ? s : DvtCSSStyle.DEFAULT_FONT_FAMILY));
  }

  //NOTE: size of textFormat is in "point" not "pixel"
  //unfortunary, Capabilities.screenDPI always returns 72
  // Default to 11px
  k = DvtCSSStyle.FONT_SIZE;
  if (! style[k]) {
    s = this.getInheritedStyle(k, style, defaultStyle);
    style.setStyle(k, DvtAfComponent.roundOff(s, DvtCSSStyle.DEFAULT_FONT_SIZE + "px"));
  }
  // Default to black
  k = DvtCSSStyle.COLOR;
  if (! style[k]) {
    s = this.getInheritedStyle(k, style, defaultStyle);
    style.setStyle(k, (s ? s : "black"));
  }

  k = DvtCSSStyle.FONT_WEIGHT;
  if (! style[k]) {
    s = this.getInheritedStyle(k, style, defaultStyle);
    if (s)
      style.setStyle(k, s);
  }

  k = DvtCSSStyle.FONT_STYLE;
  if (! style[k]) {
    s = this.getInheritedStyle(k, style, defaultStyle);
    if (s)
      style.setStyle(k, s);
  }

  k = DvtCSSStyle.TEXT_ALIGN;
  if (! style[k]) {
    s = this.getInheritedStyle(k, style, defaultStyle);
    if (s) {
      style.setStyle(k, s);
    }
    //BUG FIX #10275298 and #10274543: if no text-align is specified, CSS treats it
    //as 'left' in non-BiDi and 'right' in BiDi, so if text-align is not specified
    //here and we're in BiDi, explicitly set it to 'right'
    else if (DvtStyleUtils.isLocaleR2L()) {
      style.setStyle(k, "right");
    }
  }

  k = DvtCSSStyle.TEXT_DECORATION;
  if (! style[k]) {
    s = this.getInheritedStyle(k, style, defaultStyle);
    if (s)
      style.setStyle(k, s);
  }
  return style;
};



/**
 * @private
 */
DvtAfComponent.prototype.createCallback = function(func) {
  return DvtObj.createCallback(this, func);
};


DvtAfComponent.prototype.getHAlign = function() {
  return DvtAfComponent.HALIGN_LEFT;
};


/**
 * Get the effective halign based on the locale's reading direction.
 * 
 * @return effective halign based on locale's reading direction
 */
DvtAfComponent.prototype.getEffectiveHAlign = function() {
  var hAlign = this.getHAlign();

  if (hAlign == DvtAfComponent.HALIGN_START) {
    if (DvtStyleUtils.isLocaleR2L())
      hAlign = DvtAfComponent.HALIGN_RIGHT;
    else
      hAlign = DvtAfComponent.HALIGN_LEFT;
  }
  else if (hAlign == DvtAfComponent.HALIGN_END) {
    if (DvtStyleUtils.isLocaleR2L())
      hAlign = DvtAfComponent.HALIGN_LEFT;
    else
      hAlign = DvtAfComponent.HALIGN_RIGHT;
  }
  return hAlign;
};


/*---------------------------------------------------------------------*/
/*   CreateDisplayObject                                               */
/*---------------------------------------------------------------------*/
/**
 * @params {DvtXmlContext} xmlContext the context should contains width, height and optional fontSize and childTagHandler.
 * @return {DvtAfComponent} the root of the DvtAfComponent tree
 */
DvtAfComponent.prototype.createDisplayObj = function(context) {
  //Don't create display object if rendered is false
  if (! this.renderMe())
    return null;

  this._context = context;
  var icontext = context.getContext();

  // save context callback
  this.SaveContextCallback();

  // perf: need css style in order to truncated text
  // create computed style
  if (this.createComputedStyle)
    this.createComputedStyle();

  // create shape 
  var shape = this.createContentShape(icontext);

  if (this._hasBgShape()) {
    this._bgShape = new DvtRect(icontext, 0, 0, 0, 0, this._getUniqueId() + "_bg");

    // add to the background shape
    if (shape) {
      this.setContentShape(shape);
    }
  }
  else {
    this._bgShape = shape;
    this._shape = shape;
  }

  return this._bgShape;
};


DvtAfComponent.prototype.getDisplayObj = function() {
  return this._bgShape;
};


DvtAfComponent.prototype.addChildDisplayObjs = function(context, parentDO) {
  var child;
  var childDO;
  var cnt = this.getNumChildren();

  for (var i = 0; i < cnt; i++) {
    child = this.getChildAt(i);
    if (! child.renderMe || child.renderMe()) {
      childDO = child.createDisplayObj(context);
      if (childDO) {
        parentDO.addChild(childDO);
      }
    }
  }
};


/*---------------------------------------------------------------------*/
/*   Create Content Shape                                              */
/*---------------------------------------------------------------------*/
DvtAfComponent.prototype.createContentShape = function(icontext) {
  return null;
}

/**
 * Get the content pane where AfComponent children of this
 * AfComponent will be added.
 * In most cases the content pane will be this AfComponent itself,
 * but in AfPanelGroupLayout it is a separate sprite.  
 * 
 * @return the shape containing AfComponent children of this component
 *         
 */
DvtAfComponent.prototype.getContentShape = function() {
  return this._shape;
};

/**
 * add the shape as a child to the background shape
 */
DvtAfComponent.prototype.setContentShape = function(shape) {
  if (shape) {
    this._bgShape.addChild(shape);
    shape._prop = this._prop;
  }

  this._shape = shape;

};

/** 
 * @private
 */
DvtAfComponent.prototype.getCX = function() {
  return this._cx;
};
/** 
 * @private
 */
DvtAfComponent.prototype.setCX = function(relx) {
  this._cx = relx;
};


/** 
 * @private
 */
DvtAfComponent.prototype.getCY = function() {
  return this._cy;
};
/**
 * @private
 */
DvtAfComponent.prototype.setCY = function(rely) {
  this._cy = rely;
};



/**
 * @protected
 *
 * render background and border
 */
DvtAfComponent.prototype.renderBackground = function() {

  var style = this.getCSSStyle();

  //if no background-color, use transparent white so that
  //there's still a rect drawn at the given size, even if
  //it's not visible to the user, because it may be used
  //to define the size of a component

  //color format: rgba(r, g, b, a)
  //ex: rgba(255, 0, 0, 1)
  var bgColor = "rgba(255, 255, 255, 0)";

  var bgImgUrl;
  var bdColor;
  var bdWidth = 0;

  if (style) {
    bgColor = style[DvtCSSStyle.BACKGROUND_COLOR];
    bgImgUrl = style.getBackgroundImage();
    bdColor = style[DvtCSSStyle.BORDER_COLOR];
    bdWidth = style.getBorderWidth();
  }

  //TODO: else apply a default css style
  //should we set background color on the shape?
  var bgShape = this.getDisplayObj();
  if (bdColor) {
    bgShape.setStroke(new DvtSolidStroke(bdColor, undefined, bdWidth));
  }

  if (bgShape.setFill) {
    bgShape.setFill(new DvtSolidFill(bgColor));
  }

  if (bgImgUrl) {
    this._bgImgUrl = bgImgUrl;
    DvtImageLoader.loadImage(this._context.getContext(),
                             bgImgUrl,
                             this.createCallback(this.onBgImageLoaded));

  }
};


DvtAfComponent.prototype.onBgImageLoaded = function(image) {
  // set image size
  if (image != null && image.width && image.height) {
    var bgShape = this.getDisplayObj();
    var style = this.getCSSStyle();
    var bgImgUrl = style.getBackgroundImage();
    var bgRepeat = style[DvtCSSStyle.BACKGROUND_REPEAT];

    var dim;
    if (! bgRepeat || bgRepeat != "no-repeat") {
      dim = new DvtRectangle(0, 0, image.width, image.height);
    }
    else {
      dim = bgShape.getDimensions();
    }
    bgShape.setFill(new DvtImageFill(this._bgImgUrl, dim, bgRepeat)); 
  }
};


/**
 * @private
 * Determine to see if this component should be rendered
 */
DvtAfComponent.prototype.renderMe = function() {
  return (this instanceof DvtAfComponent &&
          this.isRendered() != "false" && this.isVisible() != "false");
};


/*---------------------------------------------------------------------*/
/*   Tooltips                                                          */
/*---------------------------------------------------------------------*/
//subclasses can override this method if necessary
//for example outputText to show full text when it is truncated
DvtAfComponent.prototype.isTooltipEnabled = function() {
  var text = this.getTooltipText();
  return text && text.length > 0;
};


//subclasses can override the tooltip text if necessary
//for example outputText to show full text when it is truncated
DvtAfComponent.prototype.getTooltipText = function() {
  return this.getShortDesc();
};

/**
 * Handle a mouse rollover to show a popup or a tooltip.
 * 
 * @param event MouseEvent
 */
DvtAfComponent.prototype.HandlePopupRollOver = function(event) {
  //don't show tooltip if tooltipText is empty string
  if (this.isTooltipEnabled()) {

    //TODO: need border color??
//     var style = this.getStyleAttr(DvtCSSStyle.COLOR);
//     if (style === undefined)
//       style = "black";

    //TODO: word wrap and truncate tooltip text?
    var ttt = this.getTooltipText();
    this.getTooltipManager().showTooltip(event.pageX, event.pageY, 
                                         ttt, this.getDisplayObj(), true);
  }
};

/**
 * Handle a mouse rollout to hide the popup
 * 
 * @param event MouseEvent
 */
DvtAfComponent.prototype.HandlePopupRollOut = function(event) {
  this.getTooltipManager().hideTooltip();
};
    

DvtAfComponent.prototype.getTooltipManager = function() {
  return this._context.getTooltipManager();
};


/**
 * @private
 * Transfer relevant attributes from the original tag to the shape element
 * @param {Object} shape  The shape element.
 */
DvtAfComponent.prototype._TransferAttributes = function(shape) {
  // dont set style unless we know the size of the component
};


/**
 * @protected
 */
DvtAfComponent.prototype.setTranslateX = function(relx) {

  this.setCX(relx);
  var dispObj = this.getDisplayObj();
  if (dispObj) {
    dispObj.setTranslateX(relx);
  }
};

/** 
 * @protected
 */
DvtAfComponent.prototype.setTranslateY = function(rely) {

  this.setCY(rely);
  var dispObj = this.getDisplayObj();
  if (dispObj) {
    dispObj.setTranslateY(rely);
  }
};


/**
 * @protected
 * Return dimensions if child is not an instance DvtAfComponent
 */
DvtAfComponent.getDimensions = function(comp) {
  if (comp instanceof DvtAfComponent) {
    return comp.getBounds();
  }
  else {
    return comp.getDimensions();
  }
}

/**
 * @protected
 * Return bounding rectangle
 */
DvtAfComponent.prototype.getBounds = function() {
  if (! this._dim) {
    var style = this.getCSSStyle();

    this._dim = new DvtRectangle(0, 0, 
                                 this._cw + style.getMarginLeft() + style.getMarginRight() + 
                                 style.getPaddingLeft() + style.getPaddingRight() + 
                                 style.getBorderLeftWidth() + style.getBorderRightWidth(),
                                 this._ch + style.getMarginTop() + style.getMarginBottom() + 
                                 style.getPaddingTop() + style.getPaddingBottom() + 
                                 style.getBorderTopWidth() + style.getBorderBottomWidth());
  }
  return this._dim;
}

DvtAfComponent._getContentShape = function(child) {
  if (child instanceof DvtAfComponent) {
    return child.getContentShape();
  }
  return child.getDisplayObj();
};


/** 
 * @private
 */
DvtAfComponent._setTranslateGroup = function(child, relx, rely) {

  if (child instanceof DvtAfComponent) {
    child._cx = relx;
    child._cy = rely;

    var dispObj = child.getDisplayObj();
    if (dispObj) {
      dispObj.setTranslateX(relx);
      dispObj.setTranslateY(rely);
    }
  }
  else {
    if (child.setTranslateX) {
      child.setTranslateX(relx);
    }
    if (child.setTranslateY) {
      child.setTranslateY(rely);
    }
  }
};


DvtAfComponent.prototype.getTagName = function() {
};

// if this component has a bg shape?
DvtAfComponent.prototype._hasBgShape = function() {
  return true;
};


/**
 * Defer text truncation and sizing
 */
DvtAfComponent.prototype.createTextShape = function(isMultiLine, text, textStyle, defStyle) {
  var context = this._context;
  var icontext = context.getContext();

//   var id = this.getId() + "_" + context.GetObjDimCnt();
  var id = this._getUniqueId();
  var textShape;

  if (isMultiLine) {
    //TODO remove if graph is ok with the text alignment changes
    // textShape = new DvtTextArea(icontext, 0, 0, id);
    textShape = new DvtTextArea(icontext, undefined, undefined, id);
  }
  else {
    textShape = new DvtText(icontext, null, 0, 0, id);
  }

  // defer text truncation 
  if (this.deferTruncation && this.deferTruncation()) {
    textShape.truncateText(DvtAfComponentFactory.DEFER_TRUNCATION);
  }

  var cssStyle = this.createTextStyles(textStyle, defStyle);
  if (cssStyle) {
    textShape.setCSSStyle(cssStyle);
  }

  textShape.setText(text);
  if (isMultiLine) {
    textShape.setWordWrap(! this.isNoWrap());
  }

  if (this.getTruncateAt) {
    textShape.setTruncateAt(this.getTruncateAt());
  }

  // add to a list of obj that need to getDimensions
  if (DvtAfComponentFactory.SIZE_ALL && this.isNoWrap && this.isNoWrap()) {
    context.AddObjShapePair(id, textShape);
  }

  if (textShape != null)
    textShape._prop = this._prop;

  //bidi???
  if (DvtStyleUtils.isLocaleR2L()) {
    DvtLayoutUtils.alignTextStart(textShape);
  }

  return textShape;
};


DvtAfComponent.prototype.isCommandComponent = function() {
  return false;
}

/*---------------------------------------------------------------------*/
/*   Event Handler                                                     */
/*---------------------------------------------------------------------*/

/** 
 * @protected
 * Handle click for the AfComponents that fire ActionEvent
 */
DvtAfComponent.prototype.HandleClickAction = function(event) {

//   if (this.getCommandComponent()) {
    //if there's an af:showPopupBehavior tag associated with
    //this component, with an action triggerType, fire PopupEvent
    //otherwise, fire ActionEvent
//     var spubs = this.getShowPopupBehaviors();

//     if (spub && spub.getTriggerType() === DvtShowPopupBehavior.TRIGGER_TYPE_ACTION) {
//        this.firePopupEvent(true);
//     }


//     if (! this.getShowPopupBehaviors()) {
//       //don't want click to fall through to rest of node
//       DvtEventManager.consumeEvent(event);

//       this.fireActionEvent(event);
//     }
//   }

};


/**
 * Adds an action event listener.
 * @param {function} listener the function to call
 * @param {string} useCapture whether the listener operates in the capture phase
 * @param {object} obj instance of the object the listener is defined on
 */
// DvtAfComponent.prototype.addActionListener = function(listener, useCapture, obj) {
//   var cmd = this.getCommandComponent();
//   if (! cmd)
//     return;

//   cmd.addEventListener(DvtActionEvent.TYPE, listener, useCapture, obj);
// }

/**
 * Removes an event listener.
 * @param {function} listener the function to call
 * @param {string} useCapture whether the listener operates in the capture phase
 * @param {object} obj instance of the object the listener is defined on
 */
// DvtAfComponent.prototype.removeActionListener = function (listener, useCapture, obj) {
//   var cmd = this.getCommandComponent();
//   if (! cmd)
//     return;

//   cmd.removeEventListener(DvtActionEvent.TYPE, listener, useCapture, obj);
// }

/**
 * Fire an action event.
 * @param {DvtMouseEvent} event
 * 
 */
// DvtAfComponent.prototype.fireActionEvent = function(event) {
//   var cmd = this.getCommandComponent();
//   if (! cmd)
//     return;

//   var aEvent = new DvtActionEvent(DvtActionEvent.SUBTYPE_ACTION, this.getId());
//   cmd.FireListener(aEvent, false);
// };


DvtAfComponent.prototype.getShowPopupBehaviors = function() {
  return this._showPopupBehaviors;
};

DvtAfComponent.prototype.addShowPopupBehavior = function(spub) {
  if (! this._showPopupBehaviors) {
    this._showPopupBehaviors = [];
  }
  this._showPopupBehaviors.push(spub);
};


DvtAfComponent.prototype._addMouseOverOutHandler = function(shape) {
  shape.addEventListener("mouseover", this.HandlePopupRollOver, false, this);
  shape.addEventListener("mouseout", this.HandlePopupRollOut, false, this);
  this._context.getEventManager().associate(shape, this);
};


DvtAfComponent.prototype.SaveContextCallback = function() {
  if ((this.getShowPopupBehaviors && this.getShowPopupBehaviors()) ||
       this.isCommandComponent()) {
    this._contextCallbackObj = this._context.getContextCallbackObj();
    this._contextCallback = this._context.getContextCallback();
  }

  /*
  var shape = this.getContentShape();
  switch (spub.getTriggerType()) {
  case DvtShowPopupBehavior.TRIGGER_TYPE_CONTEXT_MENU:
    //create custom context menu that we can listen to select event on
    //contextMenu = createContextMenu();
    break;

  case DvtShowPopupBehavior.TRIGGER_TYPE_MOUSE_HOVER:
//     this._addMouseOverOutHandler(shape);
    break;

  case DvtShowPopupBehavior.TRIGGER_TYPE_ACTION:
  default:
//     shape.addEventListener("click", this.HandleClickAction, false, this);
//     this._context.getEventManager().associate(shape, this);
    break;
  }
  */
};

DvtAfComponent.prototype.getContextCallback = function() {
  return this._contextCallback;
}

DvtAfComponent.prototype.getContextCallbackObj = function() {
  return this._contextCallbackObj;
}


// Render a DvtPropMap component (not necessary an afComponent)
DvtAfComponent._renderComp = function(context, comp, availWidth, availHeight) {

  //save available width and height
  var saveAW = context.getAvailableWidth();
  var saveAH = context.getAvailableHeight();
  context.setAvailableWidth(availWidth);
  context.setAvailableHeight(availHeight);

  if (comp.render) {
    comp.render(context);
  }
  //restore available width and height
  context.setAvailableWidth(saveAW);
  context.setAvailableHeight(saveAH);

}


// This method is called when this component is too big for its container
DvtAfComponent.prototype._addClipPath = function(context, xx, yy, ww, hh) {
  if (context.getRmIfNotFit()) {
    this._clipped = DvtAfComponent._REMOVE_LATER;
  }
  else {
    if (!ww || ! hh) {
      xx = this._cx;
      yy = this._cy;
      ww = this._cw;
      hh = this._ch;
    }

    clipPath = new DvtClipPath(this._getUniqueId() + DvtAfComponentUtils._CLIP_PATH_SUFFIX);
    clipPath.addRect(xx, yy, ww, hh);
    this.getDisplayObj().setClipPath(clipPath);
    this._clipped = DvtAfComponent._CLIP_PATH;
  }
}

DvtAfComponent.genUniqueId = function() {
  return DvtAfComponent._uniqueSeed++;
};

// get unique id
DvtAfComponent.prototype._getUniqueId = function() {
  if (this._uniqueId) {
    return this._uniqueId;
  }

  var id = this.getId();
  var rowIndex = this._context.getRowIndex();
  if (id === undefined) {
    id = "_G" + this.getTagName() + "_" + DvtAfComponent.genUniqueId();
  }
  else if (rowIndex === undefined) {
    id += "_" + DvtAfComponent.genUniqueId();
  }
  else {
    id += "_" + rowIndex;
  }

  this._uniqueId = id;
  return this._uniqueId;
}


// By default some af components such as Image don't create a background shape 
// unless this method returns a true
DvtAfComponent.prototype._needBgShape = function() {
  var style = this.getCSSStyle();
  return (style && 
          (style[DvtCSSStyle.BACKGROUND_COLOR] || style.getBackgroundImage() ||
           style[DvtCSSStyle.BORDER_COLOR] || style.getBorderWidth()));
}


DvtAfComponent.prototype.getClipped = function() {
  return this._clipped;
}


/**
 *  A static class for DvtAfComponent objects
 *  @class DvtAfComponentUtils
 *  @constructor
 */
var DvtAfComponentUtils = function() {
};


DvtAfComponentUtils._CLIP_PATH_SUFFIX = "_clp"


/**
 * Calculate the content size for vertical layout
 * If a child does not fit remove it or add clipPath
 * returns max content size and dimensions of children
 *
 * @param {DvtAfContext} context
 * @param {DvtAfComponent} afRoot The root of the afComponent tree.
 */
DvtAfComponentUtils._calcVContentSize = function(context, afRoot, arDim) {
  var cnt = afRoot.getNumChildren();
  if (cnt == 0) {
    return;
  }
  var child;
  var contentWidth = 0;
  var contentHeight = 0;
  var toBeRm = [];
  var clipPath;

  for (var i = 0; i < cnt; i++) {
    child = afRoot.getChildAt(i);

    if (! child.renderMe || child.renderMe()) {
      var dim = DvtAfComponent.getDimensions(child);
      var cw = dim.w;
      var ch = dim.h;

      // bug 13408941 remove component when size is 0
      if (cw === 0 && ch === 0) {
        toBeRm.push(child);
        continue;
      }

      //collect the component doesn't fit
      if (child._clipped || (contentHeight + ch > afRoot._ch || cw > afRoot._cw)) {
        if (child._clipped == DvtAfComponent._REMOVE_LATER || context.getRmIfNotFit()) {
          toBeRm.push(child);
          continue;
        }
        // add clip path if not exists
        else {
          if (! clipPath && child._clipped != DvtAfComponent._CLIP_PATH) {
            clipPath = new DvtClipPath(afRoot._getUniqueId() + 
                                       DvtAfComponentUtils._CLIP_PATH_SUFFIX);
            clipPath.addRect(afRoot._cx, afRoot._cy, afRoot._cw, afRoot._ch);
            afRoot.getDisplayObj().setClipPath(clipPath);
          }
          if (contentHeight + ch > afRoot._ch) {
            contentHeight = afRoot._ch;
            dim.h = afRoot._ch - contentHeight;
          }
          else {
            contentHeight += ch;
          }
          if (cw > afRoot._cw) {
            contentWidth = afRoot._cw;
            dim.w = afRoot._cw;
          }
          else {
            contentWidth = Math.max(contentWidth, cw);
          }
        }
      }
      else {
        if (! (child instanceof DvtAfSeparator)) {
          contentWidth = Math.max(contentWidth, cw);
        }
        contentHeight += ch;
      }
      arDim.push(dim);
    }
  }

  //remove child components that don't fit into the container
  for (var i = toBeRm.length - 1; i >= 0; i--) {
    child = toBeRm[i];
    DvtAfComponent.removeChild(child);    
  }

  return new DvtRectangle(0, 0, contentWidth, contentHeight);

}

/**
 * Calculate the content size for horizontal layout
 * If a child does not fit remove it or add clipPath
 * returns max content size and dimensions of children
 *
 * @param {DvtAfContext} context
 * @param {DvtAfComponent} afRoot The root of the afComponent tree.
 */
DvtAfComponentUtils._calcHContentSize = function(context, afRoot, arDim) {
  var cnt = afRoot.getNumChildren();
  if (cnt == 0) {
    return;
  }
  var child;
  var contentWidth = 0;
  var contentHeight = 0;
  var toBeRm = [];
  var clipPath;

  for (var i = 0; i < cnt; i++) {
    child = afRoot.getChildAt(i);

    if (! child.renderMe || child.renderMe()) {
      var dim = DvtAfComponent.getDimensions(child);
      var cw = dim.w;
      var ch = dim.h;

      // bug 13408941 remove component when size is 0
      if (cw === 0 && ch === 0) {
        toBeRm.push(child);
        continue;
      }

      //collect the component doesn't fit
      if (child._clipped || (contentWidth + cw > afRoot._cw || ch > afRoot._ch)) {
        if (child._clipped == DvtAfComponent._REMOVE_LATER || context.getRmIfNotFit()) {
          toBeRm.push(child);
          continue;
        }
        // add clip path if not exists
        else {
          if (! clipPath && child._clipped != DvtAfComponent._CLIP_PATH) {
            clipPath = new DvtClipPath(afRoot._getUniqueId() + 
                                       DvtAfComponentUtils._CLIP_PATH_SUFFIX);
            clipPath.addRect(afRoot._cx, afRoot._cy, afRoot._cw, afRoot._ch);
            afRoot.getDisplayObj().setClipPath(clipPath);
          }
          if (contentWidth + cw > afRoot._cw) {
            contentWidth = afRoot._cw;
            dim.w = afRoot._cw - contentWidth;
          }
          else {
            contentWidth += cw;
          }
          if (ch > afRoot._ch) {
            contentHeight = afRoot._ch;
            dim.h = afRoot._ch;
          }
          else {
            contentHeight = Math.max(contentHeight, ch);
          }
        }
      }
      else {
        contentHeight = Math.max(contentHeight, ch);
        contentWidth += cw;
      }
      arDim.push(dim);
    }
  }

  //remove child components that don't fit into the container
  for (var i = toBeRm.length - 1; i >= 0; i--) {
    child = toBeRm[i];
    DvtAfComponent.removeChild(child);    
  }

  return new DvtRectangle(0, 0, contentWidth, contentHeight);

}

/**
 * Layout the content vertically
 * If a child does not fit remove it or add clipPath
 * @param {DvtAfContext} context
 * @param {DvtAfComponent} afRoot The root of the afComponent tree.
 */
DvtAfComponentUtils.doVerticalLayout = function(context, afRoot) {
  if (! afRoot) {
    return;
  }
  var arDim = [];
  var afRootDim = DvtAfComponentUtils._calcVContentSize(context, afRoot, arDim);
  if (arDim.length == 0) {
    return;
  }

  //TODO: set on client area size (_cw and _ch) so we do not need contentSize
  //use explicit sizes if set
  //TODO: Note width and height should not include padding, border and margin
  //add afRoot._cx and afRoot._cy to account for right and bottom padding
  //around children
  afRoot._cw = afRoot.getCalcWidth(afRootDim.w);
  afRoot._ch = afRoot.getCalcHeight(afRootDim.h);

  var hAlign = afRoot.getEffectiveHAlign();
  var contentX = afRoot.getCX();
  var contentY = afRoot.getCY();
  var currY = contentY;

  var child;
  var childx;
  var childy;

  //TODO: need to get AfComponent children from content pane
  var cnt = afRoot.getNumChildren();
  for (var i = 0; i < cnt; i++) {
    if (! arDim[i])
      continue;

    child = afRoot.getChildAt(i);

    if (! child.renderMe || child.renderMe()) {
      var cw = arDim[i].w;
      var ch = arDim[i].h;

      childy = currY;

      //stretch the separator to fill the width of the layout,
      //and re-render it explicitly
      if (child instanceof DvtAfSeparator) {
        //make separator the width of the scrollable area, not the viewport
        child.setMaxWidth(afRoot._cw);
        childx = contentX;
      }
      else {
        if (hAlign == DvtAfComponent.HALIGN_LEFT) {
          childx = contentX;
        }
        else if (hAlign == DvtAfComponent.HALIGN_CENTER) {
          childx = contentX + (afRoot._cw - cw) / 2;
        }
        else if (hAlign == DvtAfComponent.HALIGN_RIGHT) {
          childx = contentX + afRoot._cw - cw;
        }
      }

      // set group translate x and y
      DvtAfComponent._setTranslateGroup(child, childx, childy);
      currY += ch;
    }
  }
}


/**
 * Layout the content horizontally
 * Layout the content of an afComponent. 
 * If a child does not fit remove it or add clipPath
 * @param {DvtAfContext} context
 * @param {DvtAfComponent} afRoot The afComponent.
 */
DvtAfComponentUtils.doHorizontalLayout = function(context, afRoot) {
  if (! afRoot) {
    return;
  }
  var arDim = [];
  var afRootDim = DvtAfComponentUtils._calcHContentSize(context, afRoot, arDim);
  if (arDim.length == 0) {
    return;
  }

  //TODO: set on client area size (_cw and _ch) so we do not need contentSize
  //use explicit sizes if set
  //TODO: Note width and height should not include padding, border and margin
  //add afRoot._cx and afRoot._cy to account for right and bottom padding
  //around children
  afRoot._cw = afRoot.getCalcWidth(afRootDim.w);
  afRoot._ch = afRoot.getCalcHeight(afRootDim.h);

  var vAlign = afRoot.getVAlign();
  var contentX = afRoot.getCX();
  var contentY = afRoot.getCY();

  var child;
  var childx;
  var childy;

  var currX;

  //BiDi for horizontal layout
  //TODO: verify the size is correct
  //if an explicit width is set, or if the content is wider than the client width,
  //then start laying out at right side of client area
  //otherwise, start laying out at right side of content width
  if (DvtStyleUtils.isLocaleR2L()) {
    currX = contentX + afRoot.getCalcWidth(afRootDim.w);
  }
  else {
    currX = contentX;
  }

  //TODO: need to get AfComponent children from content pane
  var cnt = afRoot.getNumChildren();
  for (var i = 0; i < cnt; i++) {
    if (! arDim[i])
      continue;

    child = afRoot.getChildAt(i);

    if (! child.renderMe || child.renderMe()) {

      var cw = arDim[i].w;
      var ch = arDim[i].h;

      if (vAlign == DvtAfComponent.VALIGN_TOP) {
        childy = contentY;
      }
      else if (vAlign == DvtAfComponent.VALIGN_MIDDLE) {
        childy = contentY + (afRoot._ch - ch) / 2;
      }
      else if (vAlign == DvtAfComponent.VALIGN_BOTTOM) {
        childy = contentY + afRoot._ch - ch;
      }

      //BiDi for horizontal layout
      if (DvtStyleUtils.isLocaleR2L()) {
        //subtract width of component first, because x specifies position
        //of left side of component, not right side
        currX -= cw; //child.getCalculatedWidth();
        childx = currX;
      }
      else {
        childx = currX;
        currX += cw;
      }

      // set group translate x and y
      DvtAfComponent._setTranslateGroup(child, childx, childy);
    }
  }

}

/**
 * Base class for all DvtAfComponentHandler
 * This is an abstract class, do not use directly!!!
 * @constructor
 */
var DvtAfTagHandler = function(constructor) {
  this.Init(constructor);
};

/**
 * Initializes the instance. 
 */
DvtAfTagHandler.prototype.Init = function(constructor) {
  this._constructor = constructor;
};

/**
 * @return child tag handler
 */
DvtAfTagHandler.prototype.getChildtagHandler = function() {
  return null;
};


/**
 * @protected
 * parse element and its attributes, but not children tags
 * @param {DOM Element} element
 * @return {DvtAfComponent} component
 */
DvtAfTagHandler.prototype.createComponent = function(element) {
  // create DvtAfComponent object
  var comp = this._createComponent();

  // set attributes
  if (comp) {
    comp.setProperties(element.getAttributes());
  }
  return comp;
};

/**
 * @protected
 * Add children to the parent component
 * @param {DvtAfComponent} parent component
 * @param {Array} childList a list of child component
 */
DvtAfTagHandler.prototype.addChildren = function(parent, childList) {
  if (parent &&  childList && childList.length > 0) {
    for (var i = 0; i < childList.length; i++) {
      if (childList[i]) {
        parent.addChild(childList[i]);
      }
    }
  }
};

/**
 * @protected
 * @param {DOM Element} element
 */
DvtAfTagHandler.prototype._createComponent = function() {
  // subclasses may override
  return new this._constructor();
};


/**
 * Dvt AfComponent Factory
 */
var DvtAfComponentFactory = function () {
};

/*
 * Performance flags
 */
DvtAfComponentFactory.CACHE_STYLES = false;
DvtAfComponentFactory.SIZE_ALL = true;
DvtAfComponentFactory.DEFER_TRUNCATION = true;


/**
 * @private
 */
DvtAfComponentFactory._NAME_F = "f";

DvtAfComponentFactory._registeredTags = {};

/**
 * Register a tag with DvtAfComponentFactory
 * @params {string} space the namespace of the tag
 * @params {string} tagName the name of the tag
 * @params {DvtTagHandler} tagHandler the handler for the tag
 */
DvtAfComponentFactory.registerComponent = function(space, tagName, tagHandler) {

  if (tagName && tagHandler) {
    var key = DvtAfComponentFactory._getKey(space, tagName);
    DvtAfComponentFactory._registeredTags[key] = tagHandler;
  }
};


/*-------------------------------------------------------------------------*/
/*   Parse an XML node into an AfComponent tree                            */
/*-------------------------------------------------------------------------*/
/**
 * Parses the XML node and returns the root of the DvtAfComponent tree.
 * @params {DvtXmlNode} xmlNode the XML node to parse
 * @return {DvtAfComponent} the root of the DvtAfComponent tree
 */
DvtAfComponentFactory.parseXml = function(xmlNode) {
  var childList = DvtAfComponentFactory._parseChildren(xmlNode, null);
  return (childList) ? childList[0] : null;
};


/**
 * Parses the XML element and returns the DvtAfComponent tree.
 * @params {DvtXmlNode} xml element
 * @params {DvtTagHandler} tagHandler Optional, the handler for the tag
 * @return {DvtAfComponent} the root of the DvtAfComponent tree
 */
DvtAfComponentFactory.parseElement = function(tagElement, tagHandler) {
  var tagComponent = null;

  // call the tagHandler to parse the element and create a AfTagComponent
  if (tagHandler && tagHandler.createComponent) {
    tagComponent = tagHandler.createComponent(tagElement);
  }

  // if tagHandler did not handled the tagElement, use the registered handler
  if (tagComponent == null) {
    //TODO space
    var space;
    var key = DvtAfComponentFactory._getKey(space, tagElement.getName());
    tagHandler = DvtAfComponentFactory._registeredTags[key];

    //TODO: no handler for this error
    if (! tagHandler) {
      throw "No tag handler for " + tagElement.getName();
    }
    tagComponent = tagHandler.createComponent(tagElement);
  }

  if (tagComponent != null) {
    var childTagHandler = null;

    // if there is a childTagHandler, pass it to the child tags
    if (tagHandler && tagHandler.getChildTagHandler) {
      childTagHandler = tagHandler.getChildTagHandler();
    }

    // get a list of child components
    var childList = DvtAfComponentFactory._parseChildren(tagElement, childTagHandler);

    // call the handler to add children
    tagHandler.addChildren(tagComponent, childList);
  }

  return tagComponent;
};


/**
 * @private
 * @return {DvtAfComponent} a list of child DvtAfComponent objects
 */
DvtAfComponentFactory._parseChildren = function(xmlNode, tagHandler) {
  var childList = null;

  if (xmlNode) {
    var childNodes = xmlNode.getChildNodes();
    var childComponent;
    childList = [];

    for (var i = 0; i < childNodes.length; i++) {
      var child = childNodes[i];
      var childName = child.getName();
      // only parse element
      if (child && childName != null) {
        //skip over facet tag
        if (childName === DvtAfComponentFactory._NAME_F) {
          continue;
        }
        childComponent = DvtAfComponentFactory.parseElement(child, tagHandler);
        if (childComponent) {
          childList.push(childComponent);
        }
      }
    }
  }

  return childList;
};

/**
 * @private
 * return key to the registeredTags map
 */
DvtAfComponentFactory._getKey = function(space, tagName) {
  if (space === undefined)
    space = "dvt";
  return space + ":" + tagName.toLowerCase();
};


/*---------------------------------------------------------------------*/
/*   Parse and Layout an AfComponent tree                              */
/*---------------------------------------------------------------------*/
/**
 * @param {DvtAfContext} context may contain factory, available size, fontSize and ELcontext
 * @param {DvtXmlNode} xmlNode the XML node to parse
 * @param {DvtDisplayable} parent parent of the displayable AfComponent tree
 * @return {DvtDisplayable} the root of displayable AfComponent tree
 */
DvtAfComponentFactory.parseXmlAndLayout = function(context, xmlNode, parent)
{
  // parse xmlNode into a DvtAfComponent tree
  var templateTree = DvtAfComponentFactory.parseXml(xmlNode);
  return DvtAfComponentFactory.parseAndLayout(context, templateTree, parent);
};


/*---------------------------------------------------------------------*/
/*   Parse and Layout an AfComponent tree                              */
/*---------------------------------------------------------------------*/
/**
 * @param {DvtAfContext} context may contain factory, available size, fontSize and ELcontext
 * @param {DvtAfComponent} templateTree root of the DvtAfComponent tree
 * @param {DvtDisplayable} parent parent of the displayable AfComponent tree
 * @return {DvtDisplayable} the root of displayable AfComponent tree
 */
DvtAfComponentFactory.parseAndLayout = function(context, templateTree, parent) {
  // stamp out templateTree
  // Note: if xmlstring contains EL exps, ELcontext must provide data 
  var compTree;
  if (context.getELContext())
    compTree = templateTree.stamp(context.getELContext());
  else
    compTree = templateTree;

  return DvtAfComponentFactory.doLayout(context, compTree, parent);
};



/*---------------------------------------------------------------------*/
/*   Parse and Layout an AfComponent tree                              */
/*---------------------------------------------------------------------*/
/**
 * @param {DvtAfContext} context may contain factory, available size, fontSize and ELcontext
 * @param {DvtAfComponent} templateTree root of the DvtAfComponent tree
 * @param {DvtDisplayable} parent parent of the displayable AfComponent tree
 * @return {DvtDisplayable} the root of displayable AfComponent tree
 */
DvtAfComponentFactory.doLayout = function(context, compTree, parent) {
  var dispTree = DvtAfComponentFactory._createDisplayObj(context, compTree, parent);

  // layout the displayable tree
  // Note: render code uses available size and font size if specified
  if (compTree) {
    DvtAfComponentFactory.beforeRender(context);

//     compTree.render(context); 
    DvtAfComponentFactory._render(context, dispTree, compTree);

    DvtAfComponentFactory.afterRender(context);
  }

  return dispTree;
};



//Performance: make sure using the same context for parsing, stamping
//sizing, layout and rendering
//Note: this method returns dispTree, but keep compTree in the context
DvtAfComponentFactory.parseAndStamp = function(context, templateTree, parent) {
  // stamp out templateTree
  // Note: if xmlstring contains EL exps, ELcontext must provide data 
  var compTree;
  if (context.getELContext())
    compTree = templateTree.stamp(context.getELContext());
  else
    compTree = templateTree;

  var dispTree = DvtAfComponentFactory._createDisplayObj(context, compTree, parent);

  //TODO save compTree in dispTree for now
  if (dispTree) 
    dispTree._compTree = compTree;

  return dispTree;
};


//Performance: make sure using the same context for parsing, stamping
//sizing, layout and rendering
DvtAfComponentFactory.beforeRender = function(context) {
  // getDimensions for all text components
  if (context) {
    context.GetDimensions();
  }
};


//Performance: make sure using the same context for parsing, stamping
//sizing, layout and rendering
DvtAfComponentFactory.afterRender = function(context) {
  // peform truncations on the text components that are overflow
  if (context && DvtAfComponentFactory.DEFER_TRUNCATION) {
    context.TruncateTexts();
  }
};


//Performance: make sure using the same context for parsing, stamping
//sizing, layout and rendering
DvtAfComponentFactory.layoutAndRender = function(context, dispTree) {
  if (dispTree) {
//     dispTree._compTree.render(context);

    // clear dirty flag
    context._addDirty(Number.NEGATIVE_INFINITY);

    DvtAfComponentFactory._render(context, dispTree, dispTree._compTree);
  }
};


// create displayable object tree and add to the parent
DvtAfComponentFactory._createDisplayObj = function(context, compTree, parent) {
  var dispTree;

  // create displayable object tree, 
  // Note: context must contain factory
  if (compTree) {
    dispTree = compTree.createDisplayObj(context);
  }

  // add dispTree to parent so size and metrics may be obtained
  if (parent && dispTree) {
    parent.addChild(dispTree);
  }

  return dispTree;
};


//Render the component tree
DvtAfComponentFactory._render = function(context, dispTree, compTree) {
  if (compTree) {
    compTree.render(context);

    // remove the root node if it's too big for the container
    if (context.getRmIfNotFit()) {
      var dim = DvtAfComponent.getDimensions(compTree);
      var parent = dispTree.getParent();
      if ((parent && parent.removeChild) &&
          ((dim.w === 0 && dim.h === 0) || 
           (dim.w > context.getAvailableWidth()) || 
           (dim.h > context.getAvailableHeight()) || 
           compTree.getClipped())) {
        parent.removeChild(dispTree);
      }
    }

    // if dirty flag is on, re render the component tree again
    if (context._isDirty()) {
      dispTree._compTree = compTree;

      // clear dirty flag
//      context._addDirty(Number.NEGATIVE_INFINITY);
//      compTree._render(context, dispTree, compTree);
    }


  }
};



var DvtAbstractComponent = function(context)
{
  this.Init(context);
};

DvtObj.createSubclass(DvtAbstractComponent, DvtContainer, "DvtAbstractComponent");

DvtAbstractComponent.LOAD_XML_INITIAL = "initial";

DvtAbstractComponent._ELEM_ROOT = "r";

DvtAbstractComponent.prototype.Init = function(context)
{
  DvtAbstractComponent.superclass.Init.call(this, context);
  
  this._backgroundPane = new DvtRect(context, 0, 0, 0, 0);
  this.addChild(this._backgroundPane);
  this._bAbsCompRendered = false;
};

DvtAbstractComponent.prototype.getBackgroundPane = function()
{
  return this._backgroundPane;
};

DvtAbstractComponent.prototype.renderComponent = function()
{
  this.InitComponentInternal();
  this.RenderComponentInternal();
};

DvtAbstractComponent.prototype.InitComponentInternal = function() {};

DvtAbstractComponent.prototype.RenderComponentInternal = function()
{
  //subclasses must extend
  
  if (!this._bAbsCompRendered)
  {
    this.RenderBackground();
  }
  
  this._bAbsCompRendered = true;
};

DvtAbstractComponent.prototype.loadXml = function(xmlString, eventType, paramKeys, paramValues)
{
  var parser = this.GetXmlStringParser();
  if (parser)
  {
    var rootXmlNode = parser.parse(xmlString);
    if (rootXmlNode)
    {
      if (rootXmlNode.getName() === DvtAbstractComponent._ELEM_ROOT)
        rootXmlNode = rootXmlNode.getFirstChild();
      
      if (rootXmlNode)
        this.LoadXmlDom(eventType, rootXmlNode, paramKeys, paramValues);
    }
  }
};

DvtAbstractComponent.prototype.GetXmlStringParser = function()
{
  return new DvtXmlParser(this.getContext());
};

DvtAbstractComponent.prototype.LoadXmlDom = function(eventType, rootXmlNode, paramKeys, paramValues)
{
  switch (eventType)
  {
    case DvtAbstractComponent.LOAD_XML_INITIAL:
    default:
      this.LoadXmlInitial(eventType, rootXmlNode, paramKeys, paramValues);
      break;
  }
};

DvtAbstractComponent.prototype.LoadXmlInitial = function(eventType, rootXmlNode, paramKeys, paramValues)
{
  //subclasses must override
};

DvtAbstractComponent.prototype.render = function(xmlString, width, height)
{
  this._width = width;
  this._height = height;
  
  this._backgroundPane.setWidth(this._width);
  this._backgroundPane.setHeight(this._height);

  this.loadXml(xmlString);
  this.renderComponent();

  this.getContext().queueRender();
};

DvtAbstractComponent.prototype.RenderBackground = function()
{
  var bgRgb = "#7396C8";
  var bgAlpha = 1;
  
  var arColors = ["#FFFFFF", "#FFFFFF", "#AECDEA", bgRgb];
  var arAlphas = [bgAlpha, bgAlpha, bgAlpha, bgAlpha];
  var arStops = [0, 45/255, 190/255, 1];
  
  var xx = 0;
  var yy = 0;
  var ww = this._width;
  var hh = this._height;
  var n = 1.7;
  var gw = ww*n*n;
  var gh = hh*n;
  var gx = xx+((ww-gw)/2);
  var gy = yy-35;
  
  var cx = gx + gw / 2;
  var cy = gy + gh / 2;
  //TODO: not sure how to corectly calculate single r for a non-square 
  //bounding rect...
  var r = 1.5 * Math.min(gw / 2, gh / 2);
  var arBounds = [gx, gy, gw, gh];
  
  var fill = new DvtRadialGradientFill(arColors, arAlphas, arStops, 
                                       cx, cy, r, arBounds);
  this._backgroundPane.setFill(fill);
};

DvtAbstractComponent.prototype.GetWidth = function() {
  return this._width;
}

DvtAbstractComponent.prototype.GetHeight = function() {
  return this._height;
}

var DvtAbstractPanZoomComponent = function (context) {
  this.Init(context);
};

DvtObj.createSubclass(DvtAbstractPanZoomComponent, DvtAbstractComponent, "DvtAbstractPanZoomComponent");

DvtAbstractPanZoomComponent._PANNING_OFF = 1;
DvtAbstractPanZoomComponent._ZOOMING_OFF = 2;
DvtAbstractPanZoomComponent._ZOOM_TO_FIT_OFF = 4;

//TODO: move some where else
DvtAbstractPanZoomComponent._CHANGE_LAYOUT_OFF = (1 << 29);//536870912
DvtAbstractPanZoomComponent.prototype.Init = function (context) {
  DvtAbstractPanZoomComponent.superclass.Init.call(this, context);
  this._featuresOff = 0;
};

DvtAbstractPanZoomComponent.prototype.InitComponentInternal = function () {
  this._panZoomCanvas = this.CreatePanZoomCanvas(this.GetWidth(), this.GetHeight());
  this.addChildAt(this._panZoomCanvas, 1); // Add directly on top of backgroundPane
};

DvtAbstractPanZoomComponent.prototype.CreatePanZoomCanvas = function (ww, hh) {
  var pzc = new DvtPanZoomCanvas(this.getContext(), this._buttonImages, ww, hh);
  if (this._controlPanel)
    pzc.setControlPanel(this._controlPanel);
  pzc.addEventListener(DvtPanEvent.TYPE, this.HandlePanEvent, false, this);
  pzc.addEventListener(DvtZoomEvent.TYPE, this.HandleZoomEvent, false, this);
  return pzc;
};

DvtAbstractPanZoomComponent.prototype.HandlePanEvent = function (event) {
  //do nothing; for subclasses to implement
};

DvtAbstractPanZoomComponent.prototype.HandleZoomEvent = function (event) {
  //do nothing; for subclasses to implement
};

DvtAbstractPanZoomComponent.prototype.getPanZoomCanvas = function () {
  return this._panZoomCanvas;
};

/**
 * Pan by the given amount.
 * @param {number} dx horizontal amount to pan by
 * @param {number} dy vertical amount to pan by
 * @param {DvtAnimator} animator optional animator to use to animate the pan
 */
DvtAbstractPanZoomComponent.prototype.panBy = function (dx, dy, animator) {
  this.getPanZoomCanvas().panBy(dx, dy, animator);
};

/**
 * Pan to the given position.
 * @param {number} xx horizontal position to pan to
 * @param {number} yy vertical position to pan to
 * @param {DvtAnimator} animator optional animator to use to animate the pan
 */
DvtAbstractPanZoomComponent.prototype.panTo = function (xx, yy, animator) {
  this.getPanZoomCanvas().panTo(xx, yy, animator);
};

/**
 * Zoom by the given amount.
 * @param {number} dz amount to zoom by
 * @param {number} xx horizontal center of zoom (if not specified, treated as the horizontal center of the canvas)
 * @param {number} yy vertical center of zoom (if not specified, treated as the vertical center of the canvas)
 * @param {DvtAnimator} animator optional animator to use to animate the zoom
 */
DvtAbstractPanZoomComponent.prototype.zoomBy = function (dz, xx, yy, animator) {
  this.getPanZoomCanvas().zoomBy(dz, xx, yy, animator);
};

/**
 * Zoom to the given scale.
 * @param {number} zz new scale
 * @param {number} xx horizontal center of zoom (if not specified, treated as the horizontal center of the canvas)
 * @param {number} yy vertical center of zoom (if not specified, treated as the vertical center of the canvas)
 * @param {DvtAnimator} animator optional animator to use to animate the zoom
 */
DvtAbstractPanZoomComponent.prototype.zoomTo = function (zz, xx, yy, animator) {
  this.getPanZoomCanvas().zoomTo(zz, xx, yy, animator);
};

/**
 * Zoom and pan the content pane to fit the canvas size.
 * @param {DvtAnimator} animator optional animator to use to animate the zoom-to-fit
 */
DvtAbstractPanZoomComponent.prototype.zoomToFit = function (animator) {
  this.getPanZoomCanvas().zoomToFit(animator);
};

/**
 * Get the Resources to use with this view.
 *
 * @return translated resources to use with this view
 */
DvtAbstractPanZoomComponent.prototype.getPanZoomResources = function () {
  return this._panZoomResources;
};

/**
 * Set the Resources to use with this view.
 *
 * @param resources translated resources to use with this view
 */
DvtAbstractPanZoomComponent.prototype.setPanZoomResources = function (resources) {
  this._panZoomResources = resources;
};

/**
 * Get the featuresOff
 *
 * @return featuresOff used with this view
 */
DvtAbstractPanZoomComponent.prototype.getFeaturesOff = function () {
  return this._featuresOff;
};

/**
 * Set the featuresOff
 *
 * @param flags featuresOff used with this view
 */
DvtAbstractPanZoomComponent.prototype.setFeaturesOff = function (flags) {
  this._featuresOff = flags;
};

DvtAbstractPanZoomComponent.prototype.isPanningOff = function () {
  return (this._featuresOff & DvtAbstractPanZoomComponent._PANNING_OFF) != 0;
};

DvtAbstractPanZoomComponent.prototype.isZoomingOff = function () {
  return (this._featuresOff & DvtAbstractPanZoomComponent._ZOOMING_OFF) != 0;
};

DvtAbstractPanZoomComponent.prototype.isZoomToFitOff = function () {
  return (this._featuresOff & DvtAbstractPanZoomComponent._ZOOM_TO_FIT_OFF) != 0;
};

//TODO: don't render layout combo for now
//Move this method to Diagram,er???
DvtAbstractPanZoomComponent.prototype.isChangeLayoutOff = function () {
  return true;//return (this._featuresOff & DvtAbstractPanZoomComponent._CHANGE_LAYOUT_OFF) != 0;
};

/**
 * Get the Resources to use with this view.
 *
 * @return translated resources to use with this view
 */
DvtAbstractPanZoomComponent.prototype.getResources = function () {
  return this._resources;
};

/**
 * Set the Resources to use with this view.
 *
 * @param resources translated resources to use with this view
 */
DvtAbstractPanZoomComponent.prototype.setResources = function (resources) {
  this._resources = resources;
};

DvtAbstractPanZoomComponent.prototype.setButtonImages = function (uris) {
  this._buttonImages = uris;
};

/**
 * DvtAfCommandLink
 */
var DvtAfCommandLink = function() {
  this.Init();
};

/*
 * make DvtAfCommandLink a subclass of DvtAfComponent
 */
DvtObj.createSubclass(DvtAfCommandLink, DvtAfComponent, "DvtAfCommandLink");

/*
 * Register the AfCommandLink tag with DvtAfComponentFactory
 */
DvtAfCommandLink.TAG_NAME = "cl";
DvtAfComponentFactory.registerComponent("dvt", DvtAfCommandLink.TAG_NAME, 
                                        new DvtAfTagHandler(DvtAfCommandLink));

// AfCommandLink Attributes
DvtAfCommandLink.ATTR_TEXT = "text";
DvtAfCommandLink.ATTR_DISABLED = "disabled";


/*---------------------------------------------------------------------*/
/*   AfComponent attributes                                            */
/*---------------------------------------------------------------------*/
DvtAfCommandLink.prototype.setText = function(text) {
  this.setProperty(DvtAfCommandLink.ATTR_TEXT, text);

  if (this.getContentShape()) {
    this.getContentShape().setTextString(text);
  }
};

DvtAfCommandLink.prototype.getText = function() {
  return this.getStringProp(DvtAfCommandLink.ATTR_TEXT, "");
};


DvtAfCommandLink.prototype.isDisabled = function() {
  return this.getBooleanProp(DvtAfCommandLink.ATTR_DISABLED, false);
};

DvtAfCommandLink.prototype.setDisabled = function(disabled) {
  this.setProperty(DvtAfCommandLink.ATTR_DISABLED, disabled);
};



/**
 * @private
 * Note: only need to create one instance of _afStyles per class
 */
DvtAfCommandLink.prototype.GetDefaultStyles = function() {
  return DvtAfCommandLink._afStyles;
};

/**
 * @private
 * Note: only need to create one instance of _afStyles per class
 */
DvtAfCommandLink.prototype.SetDefaultStyles = function(styleList) {
  DvtAfCommandLink._afStyles = styleList;
  if (styleList.length > 1) {
    styleList[1].mergeUnder(styleList[0]);
  }
};


/*---------------------------------------------------------------------*/
/*   Create Content Shape                                              */
/*---------------------------------------------------------------------*/
DvtAfCommandLink.prototype.createContentShape = function(icontext) {
  //if this link is disabled, use a different text color
  var textStyle = this.isDisabled() ? this.getDisabledStyle() : this.getTextStyle();

  var shape = this.createTextShape(false, this.getText(), null, textStyle);

  this._text = shape;
  return shape;
};


/**
 * @override
 */
DvtAfCommandLink.prototype.renderSelfBeforeChildren = function() {
  DvtAfCommandLink.superclass.renderSelfBeforeChildren.call(this);

  var shape = this.getContentShape();
  var bDisabled = this.isDisabled();
  if (! bDisabled && shape) {
    shape.addEventListener("click", this.getClickListenerFunction(), false, this);
    shape.addEventListener("mouseover", this.HandleRollOver, false, this);
    shape.addEventListener("mouseout", this.HandleRollOut, false, this); 

    this._context.getEventManager().associate(shape, this);
  }

  // if icon specified, load image and set image (x, y)
  var currX = DvtStyleUtils.isLocaleR2L() ? this._rightx : this._cx;
  var currY = this._cy;
  var bIcon = this.RenderIcon(currX, currY);
  var bDisabled = this.isDisabled();
  var shape = this.getContentShape();

  if (this.getText()) {
    //if this link is disabled, use a different text color
    var textStyle = bDisabled ? this.getDisabledStyle() : this.getTextStyle();

    //if the link is wider than the area allowed it, truncate the text
    var maxWidth = this._cw;
    var iconDim = this.getIconBounds();
    var iconWidth = iconDim.w;
    var rightPad = 2;

    if (bIcon) {
      var iconStyle = this.getIconStyle();
      if (iconStyle) {
        rightPad = iconStyle.getPaddingRight() + iconStyle.getBorderRightWidth() +
            iconStyle.getMarginRight();

        // For simple skin, allow some spaces between icon and button text
        if (! rightPad)
          rightPad = 3;
      }

      iconWidth += rightPad;
      maxWidth -= iconWidth;
      currX += iconWidth;
    }

    // set max width on link text
    var linkText = this._getLinkText();
    if (! linkText) {
      return;
    }
    linkText.setMaxWidth(maxWidth);
    linkText.setX(currX);
    linkText.setY(currY);

    var textDim = linkText.getCachedDims();
    var isTruncated = linkText.isTruncated();

    var ww = iconWidth + textDim.w;
    var hh = Math.max(iconDim.h, textDim.h);

    // add clip path if needed
    if (ww > this._aw || hh > this._ah) {
      this._addClipPath(this._context, this.getCX(), this.getCY(), 
                        Math.min(this._aw, ww), Math.min(this._ah, hh));
    }
    this._cw = this.getCalcWidth(ww);
    this._ch = this.getCalcHeight(hh);

    // Set X and Y position (to account for padding, etc.)
    if (bIcon) {
      this.setCX(this._cx + this.getIconBounds().w + rightPad);
    }

    // check if text isTruncated
    if (isTruncated) {
      // cached the shape for text truncation later
      if (this.deferTruncation()) {
        this._context.AddObjShapePair(shape.getId(), this._getLinkText());
      }
    }
  }

  //BiDi
  //NOTE: need to pass size explicitly because during the adjustment
  //the size will vary as things are repositioned
  this.adjustForBiDi(this._cw);

};


DvtAfCommandLink.prototype.getClickListenerFunction = function() {
  return this.HandleClickAction;
};

/**
 * Adjust the layout for BiDi.
 * 
 * @param ww original width of the link
 */
DvtAfCommandLink.prototype.adjustForBiDi = function(ww) {
  //BiDi
  //layout is currently for L2R, so make it R2L by taking the current offsets
  //from the left and mirroring them so that they are offsets from the right
  if (DvtStyleUtils.isLocaleR2L()) {
    var shape = this.getContentShape();

    if (shape) {
      this.setCX(ww - this._cx - shape.w);
    }
  }
};
    

//meant to be overridden by AfCommandImageLink subclass
DvtAfCommandLink.prototype.RenderIcon = function(x, y) {
  return false;
};

/**
 * Get icon bounds if icon is specified
 */
DvtAfCommandLink.prototype.getIconBounds = function() {
  if (this._icon) {
    return this._icon.getDimensions();
  }
  else {
    return new DvtRectangle(0, 0, 0, 0);
  }
};




DvtAfCommandLink.prototype.isClickEventPropagationStopped = function() {
  return true;
};

DvtAfCommandLink.prototype.HandleRollOver = function(event) {
  this.setUnderline(true);
};

DvtAfCommandLink.prototype.HandleRollOut = function(event) {
  this.setUnderline(false);
};


/**
 * get the link text of this component
 * @return {DvtText} the link text
 */
DvtAfCommandLink.prototype._getLinkText = function() {
  return this._text;
};

/**
 * @param:
 */
DvtAfCommandLink.prototype.setUnderline = function(bunderline) {
  var shape = this._getLinkText();
  if (shape) {
    var cssStyle = shape.getCSSStyle();
    cssStyle.setStyle("text-decoration", 
                      bunderline? "underline" : "normal");
    shape.setCSSStyle(cssStyle);
    shape.setCursor(bunderline? "pointer" : "default");
  }
};

/**
 * abstract method
 */
DvtAfCommandLink.prototype.getIconStyle = function() {
  return null;
};

/**
 * @private
 */
DvtAfCommandLink.prototype.getTextStyle = function() {
  return DvtAfCommandLink._afStyles[0];
};

/**
 * @private
 */
DvtAfCommandLink.prototype.getDisabledStyle = function() {
  return DvtAfCommandLink._afStyles[1];
};

/**
 * @override
 */
DvtAfCommandLink.prototype.ResolveEm = function(cssStyle) {
  DvtAfCommandLink.superclass.ResolveEm.call(this, cssStyle);

  // resolve disabled styles
  var disStyle = this.getDisabledStyle();
  if (disStyle) {
    this.resolveEmUnit(disStyle, this);
  }
};

DvtAfCommandLink.prototype.getTooltipText = function() {
  var text = this._getLinkText();
  if (text && text.isTruncated()) {
    return this.getText();
  }

  return DvtAfCommandLink.superclass.getTooltipText.call(this);
};



/**
 * @private
 * Transfer relevant attributes from the original tag to the shape element
 * @param {Object} shape  The shape element.

DvtAfCommandLink.prototype._TransferAttributes = function(shape) {
  //if this link is disabled, use a different text color
  var textStyle = this.isDisabled() ? this.getDisabledStyle() : this.getTextStyle();

  //set css style on the shape object
  var cssStyle = this.createTextStyles(null, textStyle);
  if (cssStyle) {
    shape.setCSSStyle(cssStyle);
  }

};
 */

/** 
 * @private
 */
DvtAfCommandLink.prototype.getTextCX = function() {
  return this.setCX(this._cx + this.getIconBounds().w + rightPad);
};


DvtAfCommandLink.prototype.getTagName = function() {
  return DvtAfCommandLink.TAG_NAME;
}

DvtAfCommandLink.prototype.deferTruncation = function() {
  return DvtAfComponentFactory.DEFER_TRUNCATION;
};


DvtAfCommandLink.prototype.isCommandComponent = function() {
  return true;
}

/**
 * DvtAfGoLink
 */
var DvtAfGoLink = function() {
  this.Init();
};

/*
 * make DvtAfGoLink a subclass of DvtAfCommandLink
 */
DvtObj.createSubclass(DvtAfGoLink, DvtAfCommandLink, "DvtAfGoLink");

/*
 * Register the AfGoLink tag with DvtAfComponentFactory
 */
DvtAfGoLink.TAG_NAME = "gl";
DvtAfComponentFactory.registerComponent("dvt", DvtAfGoLink.TAG_NAME, 
                                        new DvtAfTagHandler(DvtAfGoLink));

// AfGoLink Attributes
DvtAfGoLink.ATTR_DESTINATION = "destination";
DvtAfGoLink.ATTR_TARGET_FRAME = "targetFrame";


/*-------------------------------------------------------------------------*/
/*   AfComponent attributes                                                */
/*-------------------------------------------------------------------------*/
DvtAfGoLink.prototype.getDestination = function() {
  return this.getProperty(DvtAfGoLink.ATTR_DESTINATION);
};


DvtAfGoLink.prototype.setDestination = function(destination) {
  this.setProperty(DvtAfGoLink.ATTR_DESTINATION, destination);

  if (this.getContentShape()) {
    this.getContentShape().setDestination(destination);
  }
};

                         
//Can either specify a user-defined frame name, or use one of the following values:
//    _blank: The link opens the document in a new window.
//    _parent: The link opens the document in the window of the parent. 
//             For example, if the link appeared in a dialog window, the resulting page 
//                          would render in the parent window.
//    _self: The link opens the document in the same page or region.
//    _top: The link opens the document in a full window, replacing the entire page.

DvtAfGoLink.prototype.getTargetFrame = function() {
  return this.getProperty(DvtAfGoLink.ATTR_TARGET_FRAME, false);
};

DvtAfGoLink.prototype.setTargetFrame = function(targetFrame) {
  this.setProperty(DvtAfGoLink.ATTR_TARGET_FRAME, targetFrame);
};


/**
 * @private
 * Note: only need to create one instance of _afStyles per class
 */
DvtAfGoLink.prototype.GetDefaultStyles = function() {
  return DvtAfGoLink._afStyles;
};

/**
 * @private
 * Note: only need to create one instance of _afStyles per class
 */
DvtAfGoLink.prototype.SetDefaultStyles = function(styleList) {
  DvtAfGoLink._afStyles = styleList;
  if (styleList.length > 1) {
    styleList[1].mergeUnder(styleList[0]);
  }
};


DvtAfGoLink.prototype.renderSelfBeforeChildren = function() {
  DvtAfGoLink.superclass.renderSelfBeforeChildren.call(this);

  //whole client area is available to children, but will
  //be reduced as children are rendered
  this._acw = this._cw;
  this._ach = this._ch;

  //add "a" element around the text
  var target = this.getTargetFrame();
  var dest = this.getDestination();
  if (target || dest) {
    var shape = this._getLinkText();
    DvtGoLink.addLink(this._context.getContext(), shape, dest, target);
  }
}


DvtAfGoLink.prototype.getClickListenerFunction = function() {
  return this.handleClick;
};


//NOTE: goLink does not fire ActionEvent
DvtAfGoLink.prototype.handleClick = function(event) {
}

/**
 * Perform layout of this display object.
 * @override
 */
DvtAfGoLink.prototype.doLayout = function() {
  DvtAfComponentUtils.doVerticalLayout(this._context, this);

  /*
  var cnt = this.getNumChildren();
  if (cnt == 0) {
    // BiDi
    this.adjustForBiDi(this.getCalcWidth(this._cw));
    return;
  }

  var contentX = this.getCX();
  var contentY = this.getCY();

  //TODO: get bbox of text
  var text = this.getText();
  if (text) {
    var bbox = DvtAfComponent.getDimensions(this);
    contentY = bbox.y + bbox.h;
  }

  var maxChildWidth = (this._ew === undefined)? 0 : this._ew;
  var child;
  var arDim = [];

  //TODO: need to get AfComponent children from content pane
  for (var i = 0; i < cnt; i++) {
    child = this.getChildAt(i);

    arDim.push(DvtAfComponent.getDimensions(child));

    if (! child.renderMe || child.renderMe()) {
      if (arDim[i].w > maxChildWidth)
        maxChildWidth = arDim[i].w;
    }
  }

  var currY = contentY;
  var contentWidth = 0;
  var contentHeight = 0;
  var childx;
  var childy;

  //TODO: need to get AfComponent children from content pane
  for (i = 0; i < cnt; i++) {
    child = this.getChildAt(i);

    if (! child.renderMe || child.renderMe()) {

      var cw = arDim[i].w; //child.getCalculatedWidth();
      var ch = arDim[i].h; //child.getCalculatedHeight();

      contentWidth = Math.max(contentWidth, cw);
      childy = currY;

      if (!DvtStyleUtils.isLocaleR2L()) {
        childx = contentX;
      }
      else {
        childx = contentX + maxChildWidth - cw;
      }

      // set group translate x and y
      DvtAfComponent._setTranslateGroup(child, childx, childy);

      currY += ch;
      contentHeight += ch;
    }
  }

  // BiDi
  this.adjustForBiDi(this.getCalcWidth(contentWidth));

  //TODO: need ???
  this._cw = this.getCalcWidth(contentWidth);
  this._ch = this.getCalcHeight(contentHeight);
  */

};
    
    
DvtAfGoLink.prototype.renderChild = function(child) {
  if (child._render) {
    child._render(this._acw, this._ach);
  }

  var dim = DvtAfComponent.getDimensions(child);

  //adjust the remaining size of the client area available to 
  //children based on the size of the child just rendered
  this._ach -= dim.h;
};



/**
 * Adjust the layout for BiDi.
 * 
 * @param ww original width of the link
 */
DvtAfGoLink.prototype.adjustForBiDi = function(ww) {
  //BiDi
  //layout is currently for L2R, so make it R2L by taking the current offsets
  //from the left and mirroring them so that they are offsets from the right
  if (DvtStyleUtils.isLocaleR2L()) {
    var shape = this.getContentShape();

    if (shape) {
      this.setCX(ww - this._cx - shape.w);
    }
  }
};
    

DvtAfGoLink.prototype.isClickEventPropagationStopped = function() {
  return true;
};


/** 
 * @private
 */
DvtAfGoLink.prototype.getTextCX = function() {
  return this.setCX(this._cx + this.getIconBounds().w + rightPad);
};

DvtAfGoLink.prototype.getTagName = function() {
  return DvtAfGoLink.TAG_NAME;
}


/**
 * @private
 */
DvtAfGoLink.prototype.getTextStyle = function() {
  return DvtAfGoLink._afStyles[0];
};

/**
 * @private
 */
DvtAfGoLink.prototype.getDisabledStyle = function() {
  return DvtAfGoLink._afStyles[1];
};


/**
 * DvtAfImage
 */
var DvtAfImage = function() {
  this.Init();
};

/*
 * make DvtAfImage a subclass of DvtAfComponent
 */
DvtObj.createSubclass(DvtAfImage, DvtAfComponent, "DvtAfImage");

/*
 * Register the AfImage tag with DvtAfComponentFactory
 */
DvtAfImage.TAG_NAME = "i";
DvtAfComponentFactory.registerComponent("dvt", DvtAfImage.TAG_NAME, 
                                        new DvtAfTagHandler(DvtAfImage));

// AfImage Attributes
DvtAfImage.ATTR_SOURCE = "source";


/*-------------------------------------------------------------------------*/
/*   AfComponent attributes                                                */
/*-------------------------------------------------------------------------*/
DvtAfImage.prototype.setSrc = function(src) {
  this.setProperty(DvtAfImage.ATTR_SOURCE, src);

  if (this.getContentShape())
    this.getContentShape().setSrc(src);
};

DvtAfImage.prototype.getSrc = function() {
  return this.getProperty(DvtAfImage.ATTR_SOURCE);
};

/**
 * @private
 * Note: only need to create one instance of _afStyles per class
 */
DvtAfImage.prototype.GetDefaultStyles = function() {
  return DvtAfImage._afStyles;
};

/**
 * @private
 * Note: only need to create one instance of _afStyles per class
 */
DvtAfImage.prototype.SetDefaultStyles = function(styleList) {
  DvtAfImage._afStyles = styleList;
};


/*-------------------------------------------------------------------------*/
/*   Create Content Shape                                                  */
/*-------------------------------------------------------------------------*/
DvtAfImage.prototype.createContentShape = function(icontext) {
  // create bgShape as needed
  //Bug 13384179 - af:image border-color and border-width don't work
  this._createBgShape = this._needBgShape();
  return new DvtImage(icontext, this.getSrc(), 0, 0, 1, 1, this.getId());
};


DvtAfImage.prototype.renderSelfBeforeChildren = function() {
  DvtAfImage.superclass.renderSelfBeforeChildren.call(this);

  if (this.getSrc()) {
    var shape = this.getContentShape();
    shape.setX(this._cx);
    shape.setY(this._cy);

    DvtImageLoader.loadImage(this._context.getContext(),
                             this.getSrc(), 
                             this.createCallback(this.onImageLoaded));
  }
};

DvtAfImage.prototype.onImageLoaded = function(image) {

  // set image size
  if (image != null && image.width && image.height) {
    this._cw = this.getCalcWidth(image.width);
    this._ch = this.getCalcHeight(image.height);

    // set size on shape
    var shape = this.getContentShape();
    if (shape) {
      shape.setWidth(this._cw);
      shape.setHeight(this._ch);
    }
  }
};


DvtAfImage.prototype.getTagName = function() {
  return DvtAfImage.TAG_NAME;
}

DvtAfImage.prototype._hasBgShape = function() {
  return this._createBgShape;
}


/*
 * DvtAfMarker
 */
function DvtAfMarker() {
  this.Init();
}

/*
 * make DvtAfMarker a subclass of DvtAfComponent
 */
DvtObj.createSubclass(DvtAfMarker, DvtAfComponent, "DvtAfMarker");

/*
 * Register the DvtAfMarker tag with DvtAfComponentFactory
 */
DvtAfMarker.TAG_NAME = "mk";
DvtAfComponentFactory.registerComponent("dvt", DvtAfMarker.TAG_NAME,
                                        new DvtAfTagHandler(DvtAfMarker));

// space between marker and label
DvtAfMarker.MARKER_LABEL_PADDING = 2;

// DvtAfMarker Attributes
DvtAfMarker.ATTR_LABEL_DISPLAY = "labelDisplay";
DvtAfMarker.ATTR_LABEL_POSITION = "labelPosition";
DvtAfMarker.ATTR_LABEL_STYLE = "labelStyle";
DvtAfMarker.ATTR_VALUE = "value";
DvtAfMarker.ATTR_SCALE_X = "scaleX";
DvtAfMarker.ATTR_SCALE_Y = "scaleY";
DvtAfMarker.ATTR_SHAPE = "shape";
DvtAfMarker.ATTR_SHAPE_PATH = "shapePath";
DvtAfMarker.ATTR_FILL_COLOR = "fillColor";
DvtAfMarker.ATTR_FILL_PATTERN = "fillPattern";
DvtAfMarker.ATTR_GRADIENT_EFFECT = "gradientEffect";
DvtAfMarker.ATTR_OPACITY = "opacity";
DvtAfMarker.ATTR_WIDTH = "width";
DvtAfMarker.ATTR_HEIGHT = "height";
DvtAfMarker.ATTR_X = "x";
DvtAfMarker.ATTR_Y = "y";



// valid values for the Shape attribute. Default: circle
DvtAfMarker.CIRCLE = "c";
DvtAfMarker.SQUARE = "s";
DvtAfMarker.DIAMOND = "d";
DvtAfMarker.TRIANGLE_UP = "tu";
DvtAfMarker.TRIANGLE_DOWN = "td";
DvtAfMarker.PLUS = "p";
DvtAfMarker.HUMAN = "h";


//  Marker types (see DvtMarker)
//  Note: these marker types are defined in the same order
//        as the definitions in the parser.  Don't change
//        one without changing the other!
DvtAfMarker._markerTypeMap = {
  "c" : DvtMarker.CIRCLE,
  "s" : DvtMarker.SQUARE,
  "d" : DvtMarker.DIAMOND,
  "tu" : DvtMarker.TRIANGLE_UP,
  "td" : DvtMarker.TRIANGLE_DOWN,
  "p" : DvtMarker.PLUS,
  "h" : DvtMarker.HUMAN
};

DvtAfMarker._markerFillPatternMap = {
"smallDiagonalLeft" : DvtPatternFill.SM_DIAG_UP_LT,
"largeDiagonalLeft" : DvtPatternFill.LG_DIAG_UP_LT,
"smallDiagonalRight" : DvtPatternFill.SM_DIAG_UP_RT,
"largeDiagonalRight" : DvtPatternFill.LG_DIAG_UP_RT,
"smallCrosshatch" : DvtPatternFill.SM_CROSSHATCH,
"largeCrosshatch" : DvtPatternFill.LG_CROSSHATCH,
"smallChecker" : DvtPatternFill.SM_CHECK,
"largeChecker" : DvtPatternFill.LG_CHECK,
"smallTriangle" : DvtPatternFill.SM_TRIANGLE_CHECK,
"largeTriangle" : DvtPatternFill.LG_TRIANGLE_CHECK,
"smallDiamond" : DvtPatternFill.SM_DIAMOND_CHECK,
"largeDiamond" : DvtPatternFill.LG_DIAMOND_CHECK
};


/*-------------------------------------------------------------------------*/
/*   DvtAfMarker attributes                                                */
/*-------------------------------------------------------------------------*/
/**
 * Gets the shape of the marker. 
 * @return the shape of the marker
 */
DvtAfMarker.prototype.getShape = function() {
  return this.getProperty(DvtAfMarker.ATTR_SHAPE);
};


/**
 * Specifies the shape of the marker. 
 * @param shape the shape of the marker
 */
DvtAfMarker.prototype.setShape = function(shape) {
  this.setProperty(DvtAfMarker.ATTR_SHAPE, shape);
};

/**
 * Gets the path to the svg file to use as a custom shape
 * @return path to custom svg file
 */
DvtAfMarker.prototype.getShapePath = function() {
  return this.getProperty(DvtAfMarker.ATTR_SHAPE_PATH);
};

/**
 * Specifies the path to the svg file to use as a custom shape
 * @param shapePath path to custom svg file
 */
DvtAfMarker.prototype.setShapePath = function(shapePath) {
  this.setProperty(DvtAfMarker.ATTR_SHAPE_PATH, shapePath);
};

/**
 * Gets the value of the component. When labelDisplay="on", this value will be used as the
 * text label for this marker
 * @return value of the component
 */
DvtAfMarker.prototype.getValue = function() {
  return this.getProperty(DvtAfMarker.ATTR_VALUE);
};

/**
 * Sets the value of the component. When labelDisplay="on", this value will be used as the
 * text label for this marker
 * @param value value of the component
 */
DvtAfMarker.prototype.setValue = function(value) {
  this.setProperty(DvtAfMarker.ATTR_VALUE, value);
};

/**
 * Gets the fill color of this marker. Valid values are RGB hexadecimal
 * @return fill color of this marker
 */
DvtAfMarker.prototype.getFillColor = function() {
  return this.getProperty(DvtAfMarker.ATTR_FILL_COLOR);
};

/**
 * Sets the fill color of this marker. Valid values are RGB hexadecimal
 * @param fillColor fill color of this marker
 */
DvtAfMarker.prototype.setFillColor = function(fillColor) {
  this.setProperty(DvtAfMarker.ATTR_FILL_COLOR, fillColor);
};

/**
 * Gets the built-in pattern used to fill this marker. Valid values are:
 * "smallDiagonalLeft"
 * "smallChecker"
 * "smallDiagonalRight"
 * "smallTriangle"
 * "smallCrosshatch"
 * "smallDiamond"
 * "largeDiagonalLeft"
 * "largeChecker"
 * "largeDiagonalRight"
 * "largeTriangle"
 * "largeCrosshatch"
 * "largeDiamond"
 * 
 * The pattern is drawn with a white background, and the foreground color is taken from the fillColor attribute.
 * @return pattern used to fill the marker
 */
DvtAfMarker.prototype.getFillPattern = function() {
  return this.getProperty(DvtAfMarker.ATTR_FILL_PATTERN);
};

/**
 * Sets the built-in pattern used to fill this marker. Valid values are:
 * "smallDiagonalLeft"
 * "smallChecker"
 * "smallDiagonalRight"
 * "smallTriangle"
 * "smallCrosshatch"
 * "smallDiamond"
 * "largeDiagonalLeft"
 * "largeChecker"
 * "largeDiagonalRight"
 * "largeTriangle"
 * "largeCrosshatch"
 * "largeDiamond"
 * 
 * The pattern is drawn with a white background, and the foreground color is taken from the fillColor attribute.
 * @param pattern pattern used to fill the marker
 */
DvtAfMarker.prototype.setFillPattern = function(fillPattern) {
  this.setProperty(DvtAfMarker.ATTR_FILL_PATTERN, fillPattern);
};

/**
 * Gets the horizontal scale factor for this marker. This determines how much to scale the marker from its default size.
 * @return horizontal scale factor
 */
DvtAfMarker.prototype.getScaleX = function() {
  return this.getProperty(DvtAfMarker.ATTR_SCALE_X);
};

/**
 * Sets the horizontal scale factor for this marker. This determines how much to scale the marker from its default size.
 * @param scaleX horizontal scale factor
 */
DvtAfMarker.prototype.setScaleX = function(scaleX) {
  this.setProperty(DvtAfMarker.ATTR_SCALE_X, scaleX);
};

/**
 * Gets the vertical scale factor for this marker. This determines how much to scale the marker from its default size.
 * @return vertical scale factor
 */
DvtAfMarker.prototype.getScaleY = function() {
  return this.getProperty(DvtAfMarker.ATTR_SCALE_Y);
};

/**
 * Gets the vertical scale factor for this marker. This determines how much to scale the marker from its default size.
 * @param scaleY vertical scale factor
 */
DvtAfMarker.prototype.setScaleY = function(scaleY) {
  this.setProperty(DvtAfMarker.ATTR_SCALE_Y, scaleY);
};

/**
 * Gets the font style for labels associated with this marker. Accepts font-related CSS attributes like font-name, 
 * font-weight, font-size, color, etc.
 * @return font style for labels associated with this marker
 */
DvtAfMarker.prototype.getLabelStyle = function() {
  return this.getProperty(DvtAfMarker.ATTR_LABEL_STYLE);
};

/**
 * Sets the font style for labels associated with this marker. Accepts font-related CSS attributes like font-name, 
 * font-weight, font-size, color, etc.
 * @param labelStyle font style for labels associated with this marker
 */
DvtAfMarker.prototype.setLabelStyle = function(labelStyle) {
  this.setProperty(DvtAfMarker.ATTR_LABEL_STYLE, labelStyle);
};

/**
 * Gets the position relative to the marker that the specified value label should be displayed at. Valid values are:
 * "top" - Display the label on top of the marker
 * "bottom" - Display the label below the marker
 * "center" - (default) Display the label on the center of the marker
 * @return position of the marker's label
 */
DvtAfMarker.prototype.getLabelPosition = function() {
  return this.getProperty(DvtAfMarker.ATTR_LABEL_POSITION);
};

/**
 * Sets the position relative to the marker that the specified value label should be displayed at. Valid values are:
 * "top" - Display the label on top of the marker
 * "bottom" - Display the label below the marker
 * "center" - (default) Display the label on the center of the marker
 * @param labelPosition position of the marker's label
 */
DvtAfMarker.prototype.setLabelPosition = function(labelPosition) {
  this.setProperty(DvtAfMarker.ATTR_LABEL_POSITION, labelPosition);
};

/**
 * Gets the label display behavior for this marker. Valid values are:
 * "on" - Display the label provided in the value attribute 
 * "off" - (default) Do not display the label provided in the value attribute
 * @return label display behavior for this marker
 */
DvtAfMarker.prototype.getLabelDisplay = function() {
  return this.getProperty(DvtAfMarker.ATTR_LABEL_DISPLAY);
};

/**
 * Sets the label display behavior for this marker. Valid values are:
 * "on" - Display the label provided in the value attribute 
 * "off" - (default) Do not display the label provided in the value attribute
 * @param labelDisplay label display behavior for this marker
 */
DvtAfMarker.prototype.setLabelDisplay = function(labelDisplay) {
  this.setProperty(DvtAfMarker.ATTR_LABEL_DISPLAY, labelDisplay);
};

/**
 * Gets the gradient effect that should be used for this marker. Valid values are:
 * "auto" - (default) Sets a gradient on the marker
 * "none" - Do not use gradients on the marker. This causes markers to appear flat.
 * @return gradient effect for this marker
 */
DvtAfMarker.prototype.getGradientEffect = function() {
  return this.getProperty(DvtAfMarker.ATTR_GRADIENT_EFFECT);
};

/**
 * Specifies the gradient effect that should be used for this marker. Valid values are:
 * "auto" - (default) Sets a gradient on the marker
 * "none" - Do not use gradients on the marker. This causes markers to appear flat.
 * @param gradientEffect gradient effect for this marker
 */
DvtAfMarker.prototype.setGradientEffect = function(gradientEffect) {
  this.setProperty(DvtAfMarker.ATTR_GRADIENT_EFFECT, gradientEffect);
};

/**
 * Gets the opacity of the fill color of the marker. Valid values are from 0.0 - 1.0, where 0.0 is transparent and 1.0 is opaque.
 * @return opacity of the marker
 */
DvtAfMarker.prototype.getOpacity = function() {
  return this.getProperty(DvtAfMarker.ATTR_OPACITY);
};

/**
 * Specifies the opacity of the fill color of the marker. Valid values are from 0.0 - 1.0, where 0.0 is transparent and 1.0 is opaque.
 * @param opacity opacity of the marker
 */
DvtAfMarker.prototype.setOpacity = function(opacity) {
  this.setProperty(DvtAfMarker.ATTR_OPACITY, opacity);
};


DvtAfMarker.prototype.isRendered = function() {
  return this.getProperty(DvtAfComponent.ATTR_RENDERED);
};

DvtAfMarker.prototype.setRendered = function(rendered) {
  this.setProperty(DvtAfComponent.ATTR_RENDERED, rendered);
};

DvtAfMarker.prototype.getShortDesc = function() {
  return this.getProperty(DvtAfComponent.ATTR_SHORT_DESC);
};

DvtAfMarker.prototype.setShortDesc = function(shortDesc) {
  this.setProperty(DvtAfComponent.ATTR_SHORT_DESC, shortDesc);
};

/**
 * Gets the width for this marker. 
 * @return width 
 */
DvtAfMarker.prototype.getWidth = function() {
  return this.getFloatProp(DvtAfMarker.ATTR_WIDTH);
};

/**
 * Sets the width for this marker. 
 * @param width 
 */
DvtAfMarker.prototype.setWidth = function(width) {
  this.setProperty(DvtAfMarker.ATTR_WIDTH, width);
};

/**
 * Gets the height for this marker.
 * @return height
 */
DvtAfMarker.prototype.getHeight = function() {
  return this.getFloatProp(DvtAfMarker.ATTR_HEIGHT);
};

/**
 * Gets the height for this marker.
 * @param height 
 */
DvtAfMarker.prototype.setHeight = function(height) {
  this.setProperty(DvtAfMarker.ATTR_HEIGHT, height);
};


DvtAfMarker.prototype.getX = function() {
  return this.getFloatProp(DvtAfMarker.ATTR_X, 0);
};

DvtAfMarker.prototype.getY = function() {
  return this.getFloatProp(DvtAfMarker.ATTR_Y, 0);
};



/*-------------------------------------------------------------------------*/
/*   CreateDisplayObject                                                   */
/*-------------------------------------------------------------------------*/
DvtAfMarker.prototype.createDisplayObj = function(context) {
  //Bug 13477511 - RENDERED FLAG ISN'T WORKING CORRECTLY FOR MARKERS
  if (! this.renderMe())
    return null;

  this._context = context;
  var icontext = context.getContext()

  // if it has an Id, make it unique
  // otherwise, generate a unique Id
  var id = this._getUniqueId();
//   if (! id) {
//     id = "_Gmk";
//   }
//   id += ("_" + context.GetObjDimCnt());

  var ww = this.getWidth();
  var hh = this.getHeight();
  var x = 0;
  var y = 0;


  // if shapePath is specified, find the custom marker def and use it
  //Note: in graph mt="s,10" or mt="/resources/svg/cherry.svg,9"

  // mtype is used as a svg path for custom marker or id for built-in shape
  var mtype = this.getShapePath();
  var type;

  if (! mtype) {
    var mshape = this.getShape();
    // no svgPath and no shape specified, default to circle
    if (! mshape) {
      mshape = DvtAfMarker.CIRCLE;
    }
    type = DvtAfMarker._markerTypeMap[mshape];
    mtype = id;

    if (! ww) {
      ww = DvtMarkerDef.BI_DEFAULT_MARKER_SIZE;
    }
    if (! hh) {
      hh = DvtMarkerDef.BI_DEFAULT_MARKER_SIZE;
    }
  }
  else {
    type = DvtMarker.CUSTOM;
    x = this.getX();
    y = this.getY();
  }

  //NOTE: get dimensions seems ignore scale
  //resolve scaleX and scaleY -
  //For builtin marker: width and height are scaled here
  var scaleX = this.getScaleX();
  var scaleY = this.getScaleY();

  // create marker
  var marker = new DvtMarker(icontext, type, x, y, ww, hh, mtype, scaleX, scaleY);

  marker._prop = this._prop;
  
  // solid, gradient, pattern fill?
  var fc = this.getFillColor();
  var fa = this.getOpacity();
  var ge = this.getGradientEffect();
  var fp = this.getFillPattern();

  if (fp) {
    fp = DvtAfMarker._markerFillPatternMap[fp];
  }
  if (fp) {
    marker.setFill(new DvtPatternFill(fp, fc));
  }
  //TODO how user specfies marker linear/radial gradient?
  else if (ge == "auto") {
    marker.setFill(new DvtGradientFill([fc], fa ? [fa] : fa));
  }
  else if (fc || fa) {
    marker.setFill(new DvtSolidFill(fc, fa));
  }

  this._marker = marker;

  // create marker label
  var value = this.getValue();
  var bvis = (this.getLabelDisplay() && this.getLabelDisplay() === "off") ? false : true;

  if (value && bvis) {
    var labelId = id + "_lb";
    var label = new DvtText(icontext, value, 0, 0, labelId);

    // add to a list of obj that need to getDimensions
    if (DvtAfComponentFactory.SIZE_ALL) {
      context.AddObjShapePair(labelId, label);
    }
    var cssStyle;
    var lstyle = this.getLabelStyle();
    if (DvtAfComponentFactory.CACHE_STYLES) {
      cssStyle = context.GetCSSStyle(this.getTagName(), "", lstyle, true);
    }
    else {
      cssStyle = lstyle ? new DvtCSSStyle(lstyle) : DvtCSSStyle.createDefaultTextStyle();
    }

    if (cssStyle) {
      label.setCSSStyle(cssStyle);
    }
    this._label = label;

    // create a container for both marker and label
    var group = new DvtContainer(icontext, id);
    var position = this.getLabelPosition();
    if (position === "top") {
      group.addChild(label);
      group.addChild(marker);
    }
    else {
      group.addChild(marker);
      group.addChild(label);
    }
    this._bgShape = group;
  }
  else {
    this._bgShape = marker;
  }
  return this._bgShape;
}


//extends DvtAfComponent
// DvtAfMarker.prototype.getDisplayObj = function() {
//   return this._bgShape;
// }

DvtAfMarker.prototype.getMarkerDisplayObj = function() {
  return this._marker;
}

//extends DvtAfComponent TODO: comment out?
DvtAfMarker.prototype.setTranslateX = function(relx) {
  this._bgShape.setTranslateX(relx);
}

//extends DvtAfComponent TODO: comment out?
DvtAfMarker.prototype.setTranslateY = function(rely) {
  this._bgShape.setTranslateY(rely);
}


DvtAfMarker.prototype.render = function(context) {
  //Bug 13477511 - RENDERED FLAG ISN'T WORKING CORRECTLY FOR MARKERS
  if (! this.renderMe())
    return;

  var marker = this._marker;
  var label = this._label;
  var mkx = this.getX();
  var mky = this.getY();

  var markerSize = marker.getSize();

  // no visible label
  if (! label) {
    //extends DvtAfComponent
    this._setBounds(context, mkx, mky, markerSize + mkx, markerSize + mky);
    return;
  }

  //Bug 13408941 - diagrammer js code stops executing when markersize is zero
  if (markerSize === 0) {
    var lbdim = label.getDimensions();
    this._setBounds(context, mkx, mky, lbdim.w + mkx, lbdim.h + mky);
    return;
  }

  var mktx = marker.getTranslateX() || 0;
  var mkty = marker.getTranslateY() || 0;
  var markerDim = {
    x : (mkx + mktx),
    y : (mky + mkty),
    w : markerSize,
    h : markerSize
  };

  var labelDim;
  if (DvtAfComponentFactory.SIZE_ALL) {
    labelDim = label.getCachedDims();
  }
  if (! (labelDim instanceof DvtRectangle)) {
    labelDim = label.getDimensions();
  }

  var lbtx = markerDim.x;
  var lbty = markerDim.y;
  label.setTranslateX(lbtx)
  label.setTranslateY(lbty)
  var gww;
  var ghh;
  var position = this.getLabelPosition();
  var wM_L = markerDim.w - labelDim.w;
  var hM_L = markerDim.h - labelDim.h;

  switch (position) {
  case "top":
    if (wM_L > 0) {
      label.setTranslateX(lbtx + wM_L/2);
    }
    else {
      marker.setTranslateX(mktx - wM_L/2);
    }
    marker.setTranslateY(mkty + labelDim.h + 
                         DvtAfMarker.MARKER_LABEL_PADDING);

    gww = Math.max(markerDim.w, labelDim.w);
    ghh = labelDim.h + DvtAfMarker.MARKER_LABEL_PADDING + markerDim.h;
    break;

  case "bottom":
    if (wM_L > 0) {
      label.setTranslateX(lbtx + wM_L/2);
    }
    else {
      marker.setTranslateX(mktx - wM_L/2);
    }
    label.setTranslateY(lbty + markerDim.h + 
                        DvtAfMarker.MARKER_LABEL_PADDING);

    gww = Math.max(markerDim.w, labelDim.w);
    ghh = markerDim.h + DvtAfMarker.MARKER_LABEL_PADDING + labelDim.h;
    break;

  case "left":
    if (hM_L > 0) {
      label.setTranslateY(lbty + hM_L/2);
    }
    else {
      marker.setTranslateY(mkty - hM_L/2);
    }
    marker.setTranslateX(mktx + labelDim.w + 
                         DvtAfMarker.MARKER_LABEL_PADDING);

    gww = labelDim.w + DvtAfMarker.MARKER_LABEL_PADDING + markerDim.w;
    ghh = Math.max(markerDim.h, labelDim.h);
    break;

  case "right":
    if (hM_L > 0) {
      label.setTranslateY(lbty + hM_L/2);
    }
    else {
      marker.setTranslateY(mkty - hM_L/2);
    }
    label.setTranslateX(lbtx + markerDim.w + 
                        DvtAfMarker.MARKER_LABEL_PADDING);

    gww = markerDim.w + DvtAfMarker.MARKER_LABEL_PADDING + labelDim.w;
    ghh = Math.max(markerDim.h, labelDim.h);
    break;

  case "center":
  default:
    if (wM_L > 0) {
      label.setTranslateX(lbtx + wM_L/2);
    }
    else {
      marker.setTranslateX(mktx - (wM_L/2));
    }
    if (hM_L > 0) {
      label.setTranslateY(lbty + hM_L/2);
    }
    else {
      marker.setTranslateY(mkty - (hM_L/2));
    }
    gww = Math.max(markerDim.w, labelDim.w);
    ghh = Math.max(markerDim.h, labelDim.h);
    break;
  }

  //extends DvtAfComponent
  this._setBounds(context, lbtx, lbty, gww + lbtx, ghh + lbty);

};


//extends DvtAfComponent
// DvtAfMarker.prototype.getDimensions = function() {
//   return this._groupDim;
// }



DvtAfMarker.prototype.getTagName = function() {
  return DvtAfMarker.TAG_NAME;
}



//extends DvtAfComponent
/**
 * @private
 * Note: only need to create one instance of _afStyles per class
 */
DvtAfMarker.prototype.GetDefaultStyles = function() {
  return DvtAfMarker._afStyles;
};

//extends DvtAfComponent
/**
 * @private
 * Note: only need to create one instance of _afStyles per class
 */
DvtAfMarker.prototype.SetDefaultStyles = function(styleList) {
  DvtAfMarker._afStyles = styleList;
};

//extends DvtAfComponent
/**
 * @private
 */
DvtAfMarker.prototype._render = function(availWidth, availHeight) {
  if (! this.renderMe())
    return;

  //if shortDesc is specified, show it in a tooltip
  if (this.getShortDesc()) {
    this._addMouseOverOutHandler(this.getDisplayObj());
  }

  //Performance: using the same context during rendering
  //so we have a list of dimensions for all text objects
  var context = this._context;
  DvtAfComponent._renderComp(context, this, availWidth, availHeight);
};

//extends DvtAfComponent
DvtAfMarker.prototype.renderSelfBeforeChildren = function() {
};


DvtAfMarker.prototype._setBounds = function(context, xx, yy, ww, hh) {
  this._dim = new DvtRectangle(xx, yy, ww, hh);

  // clipped?
  if (this._dim.w > context.getAvailableWidth() || 
      this._dim.h > context.getAvailableHeight()) {

    this._addClipPath(context, 0, 0,
                      Math.min(context.getAvailableWidth(), this._dim.w), 
                      Math.min(context.getAvailableHeight(), this._dim.h));
  }
}
/**
 * DvtAfOutputText
 */
var DvtAfOutputText = function() {
  this.Init();
};

/*
 * make DvtAfOutputText a subclass of DvtAfComponent
 */
DvtObj.createSubclass(DvtAfOutputText, DvtAfComponent, "DvtAfOutputText");

/*
 * Register the AfOutputText tag with DvtAfComponentFactory
 */
DvtAfOutputText.TAG_NAME = "ot";
DvtAfComponentFactory.registerComponent("dvt", DvtAfOutputText.TAG_NAME, 
                                        new DvtAfTagHandler(DvtAfOutputText));


// AfOutputText Attributes
DvtAfOutputText.ATTR_VALUE = "value";
DvtAfOutputText.ATTR_NO_WRAP = "noWrap";
DvtAfOutputText.ATTR_TRUNCATE_AT = "truncateAt";




/*-------------------------------------------------------------------------*/
/*   AfComponent attributes                                                */
/*-------------------------------------------------------------------------*/
DvtAfOutputText.prototype.setNoWrap = function(noWrap) {
  this.setProperty(DvtAfOutputText.ATTR_NO_WRAP, noWrap);
};

// default: wrapping
DvtAfOutputText.prototype.isNoWrap = function() {
  return this.getBooleanProp(DvtAfOutputText.ATTR_NO_WRAP, false);
};

DvtAfOutputText.prototype.setTruncateAt = function(truncateAt) {
  this.setProperty(DvtAfOutputText.ATTR_TRUNCATE_AT, truncateAt);
};

// default: no truncation
DvtAfOutputText.prototype.getTruncateAt = function() {
  return this.getIntegerProp(DvtAfOutputText.ATTR_TRUNCATE_AT, 0);
};

DvtAfOutputText.prototype.setValue = function(value) {
  this.setProperty(DvtAfOutputText.ATTR_VALUE, value);

  if (this.getContentShape()) {
    this.getContentShape().setText(value);
  }
};

DvtAfOutputText.prototype.getValue = function() {
  return this.getProperty(DvtAfOutputText.ATTR_VALUE);
};


/**
 * @private
 * Note: only need to create one instance of _afStyles per class
 */
DvtAfOutputText.prototype.GetDefaultStyles = function() {
  return DvtAfOutputText._afStyles;
};

/**
 * @private
 * Note: only need to create one instance of _afStyles per class
 */
DvtAfOutputText.prototype.SetDefaultStyles = function(styleList) {
  DvtAfOutputText._afStyles = styleList;
};


/*-------------------------------------------------------------------------*/
/*   Create Content Shape                                                  */
/*-------------------------------------------------------------------------*/
DvtAfOutputText.prototype.createContentShape = function(icontext) {
  this._createBgShape = this._needBgShape();
  return this.createTextShape(true, this.getValue(), null);
};


/**
 * @override
 */
DvtAfOutputText.prototype.renderSelfBeforeChildren = function() {
  DvtAfOutputText.superclass.renderSelfBeforeChildren.call(this);

  if (this.getValue()) {
    var shape = this.getContentShape();
    var dim;

    // set max width
    shape.setMaxWidth(this._cw);
    shape.setX(this.getCX());
    shape.setY(this.getCY());

    dim = shape.getCachedDims();
    var isTruncated = shape.isTruncated();

    // add clip path if needed
    if (! isTruncated && dim.w > this._aw || dim.h > this._ah) {
      this._addClipPath(this._context, this.getCX(), this.getCY(), 
                        Math.min(this._aw, dim.w), Math.min(this._ah, dim.h));
    }
    this._cw = this.getCalcWidth(dim.w);
    this._ch = this.getCalcHeight(dim.h);

    // check if text isTruncated
    if (isTruncated) {
      // cached the shape for text truncation later
      if (this.deferTruncation()) {
        this._context.AddObjShapePair(shape.getId(), shape);
      }
      shape.addEventListener("mouseover", this.HandlePopupRollOver, false, this);
      shape.addEventListener("mouseout", this.HandlePopupRollOut, false, this);          
    }
  }
};


DvtAfOutputText.prototype.getTooltipText = function() {
  var shape = this.getContentShape();
  if (shape && shape.isTruncated()) {
    return this.getValue();
  }
  return DvtAfOutputText.superclass.getTooltipText.call(this);
}


DvtAfOutputText.prototype.getTagName = function() {
  return DvtAfOutputText.TAG_NAME;
}


DvtAfOutputText.prototype._hasBgShape = function() {
  return this._createBgShape;
}


DvtAfOutputText.prototype.deferTruncation = function() {
  return DvtAfComponentFactory.DEFER_TRUNCATION && this.isNoWrap();
};


/**
 * DvtAfOutputFormatted
 *
outputFormatted supports the following HTML markup:
    * <br>
    * <hr>
    * <li>, <ol>, <ul>
    * <p>
    * <b>
    * <i>
    * <tt>
    * <big>
    * <small>
    * <pre>
    * <span>
    * <a>

and the following entities:
    * &lt;
    * &gt;
    * &amp;
    * &reg;
    * &copy;
    * &nbsp;
    * &quot;
    *
 */
var DvtAfOutputFormatted = function() {
  this.Init();
};

/*
 * make DvtAfOutputFormatted a subclass of DvtAfComponent
 */
DvtObj.createSubclass(DvtAfOutputFormatted, DvtAfComponent, "DvtAfOutputFormatted");

/*
 * Register the AfOutputFormatted tag with DvtAfComponentFactory
 */
DvtAfOutputFormatted.TAG_NAME = "otf";
DvtAfComponentFactory.registerComponent("dvt", DvtAfOutputFormatted.TAG_NAME, 
                                        new DvtAfTagHandler(DvtAfOutputFormatted));


// AfOutputFormatted Attributes
DvtAfOutputFormatted.ATTR_VALUE = "value";


/*-------------------------------------------------------------------------*/
/*   AfComponent attributes                                                */
/*-------------------------------------------------------------------------*/
DvtAfOutputFormatted.prototype.setValue = function(value) {
  this.setProperty(DvtAfOutputFormatted.ATTR_VALUE, value);

  if (this.getContentShape()) {
    this.getContentShape().setText(value);
  }
};

DvtAfOutputFormatted.prototype.getValue = function() {
  return this.getProperty(DvtAfOutputFormatted.ATTR_VALUE);
};


/**
 * @private
 * Note: only need to create one instance of _afStyles per class
 */
DvtAfOutputFormatted.prototype.GetDefaultStyles = function() {
  return DvtAfOutputFormatted._afStyles;
};

/**
 * @private
 * Note: only need to create one instance of _afStyles per class
 */
DvtAfOutputFormatted.prototype.SetDefaultStyles = function(styleList) {
  DvtAfOutputFormatted._afStyles = styleList;
};


/*-------------------------------------------------------------------------*/
/*   Create Content Shape                                                  */
/*-------------------------------------------------------------------------*/
DvtAfOutputFormatted.prototype.createContentShape = function(icontext) {
  var context = this._context;
  var id = this._getUniqueId();
  textShape = new DvtTextFormatted(icontext, 0, 0, id);

  var cssStyle = this.createTextStyles(null, null);
  if (cssStyle) {
    textShape.setCSSStyle(cssStyle);
  }

  textShape.setText(this.getValue());

  // add to a list of obj that need to getDimensions
//   if (DvtAfComponentFactory.SIZE_ALL) {
//     context.AddObjShapePair(id, textShape);
//   }

  return textShape;
};


DvtAfOutputFormatted.prototype.renderSelfBeforeChildren = function() {
  DvtAfOutputFormatted.superclass.renderSelfBeforeChildren.call(this);

    var shape = this.getContentShape();

    // set max width
    shape.setMaxWidth(this._cw);
    shape.setX(this.getCX());
    shape.setY(this.getCY());

    var dim = shape.getCachedDims();
    var isTruncated = shape.isTruncated();

    // add clip path if needed
    if (! isTruncated && dim.w > this._aw || dim.h > this._ah) {
      this._addClipPath(this._context, this.getCX(), this.getCY(), 
                        Math.min(this._aw, dim.w), Math.min(this._ah, dim.h));
    }

    this._cw = this.getCalcWidth(dim.w);
    this._ch = this.getCalcHeight(dim.h);

    // check if text isTruncated
    if (shape.isTruncated && shape.isTruncated()) {
      shape.addEventListener("mouseover", this.HandlePopupRollOver, false, this);
      shape.addEventListener("mouseout", this.HandlePopupRollOut, false, this);          
    }

};


DvtAfOutputFormatted.prototype.getTagName = function() {
  return DvtAfOutputFormatted.TAG_NAME;
}

DvtAfOutputFormatted.prototype._hasBgShape = function() {
  return DvtAfComponent._CREATE_BG_SHAPE;
}

/**
 * DvtAfPanelGroupLayout
 * Available client size
 *  _acw 
 *  _ach
 *
 */
var DvtAfPanelGroupLayout = function() {
  this.Init();
};


/*
 * make DvtAfPanelGroupLayout a subclass of DvtAfComponent
 */
DvtObj.createSubclass(DvtAfPanelGroupLayout, DvtAfComponent, "DvtAfPanelGroupLayout");

/*
 * Register the AfImage tag with DvtAfComponentFactory
 */
DvtAfPanelGroupLayout.TAG_NAME = "pgl";
DvtAfComponentFactory.registerComponent("dvt", DvtAfPanelGroupLayout.TAG_NAME, 
                                        new DvtAfTagHandler(DvtAfPanelGroupLayout));

// AfPanelGroupLayout Attributes
DvtAfPanelGroupLayout.ATTR_LAYOUT = "layout";
DvtAfPanelGroupLayout.ATTR_VALIGN = "valign";
DvtAfPanelGroupLayout.ATTR_HALIGN = "halign";


DvtAfPanelGroupLayout.LAYOUT_VERTICAL = "vertical";
DvtAfPanelGroupLayout.LAYOUT_HORIZONTAL = "horizontal";
DvtAfPanelGroupLayout.LAYOUT_SCROLL = "scroll";



/*-------------------------------------------------------------------------*/
/*   AfComponent attributes                                                */
/*-------------------------------------------------------------------------*/
DvtAfPanelGroupLayout.prototype.getLayout = function() {
  return this.getStringProp(DvtAfPanelGroupLayout.ATTR_LAYOUT, 
                            DvtAfPanelGroupLayout.LAYOUT_HORIZONTAL);
};

DvtAfPanelGroupLayout.prototype.setLayout = function(layout) {
  this.setProperty(DvtAfPanelGroupLayout.ATTR_LAYOUT, layout);
};

DvtAfPanelGroupLayout.prototype.getHAlign = function() {
  return this.getStringProp(DvtAfPanelGroupLayout.ATTR_HALIGN,
                            DvtAfComponent.HALIGN_START);
};

DvtAfPanelGroupLayout.prototype.setHAlign = function(hAlign) {
  this.setProperty(DvtAfPanelGroupLayout.ATTR_HALIGN, hAlign);
};

DvtAfPanelGroupLayout.prototype.getVAlign = function() {
  return this.getStringProp(DvtAfPanelGroupLayout.ATTR_VALIGN,
                            DvtAfComponent.VALIGN_TOP);
};

DvtAfPanelGroupLayout.prototype.setVAlign = function(vAlign) {
  this.setProperty(DvtAfPanelGroupLayout.ATTR_VALIGN, vAlign);
};


/**
 * @private
 * Note: only need to create one instance of _afStyles per class
 */
DvtAfPanelGroupLayout.prototype.GetDefaultStyles = function() {
  return DvtAfPanelGroupLayout._afStyles;
};

/**
 * @private
 * Note: only need to create one instance of _afStyles per class
 */
DvtAfPanelGroupLayout.prototype.SetDefaultStyles = function(styleList) {
  DvtAfPanelGroupLayout._afStyles = styleList;
};


/*-------------------------------------------------------------------------*/
/*   CreateDisplayObject                                                   */
/*-------------------------------------------------------------------------*/
DvtAfPanelGroupLayout.prototype.createDisplayObj = function(context) {
  var dispObj = DvtAfPanelGroupLayout.superclass.createDisplayObj.call(this, context);

  this.addChildDisplayObjs(context, this._contentPane);

  return dispObj;
};


/*-------------------------------------------------------------------------*/
/*   Create Content Shape                                                  */
/*-------------------------------------------------------------------------*/
DvtAfPanelGroupLayout.prototype.createContentShape = function(icontext) {
  this._contentPane = new DvtRect(icontext, 0, 0, 0, 0, this.getId() + "_cp");

  var style = this.getCSSStyle();
  this._contentPane.setCSSStyle(style);

  return this._contentPane;
};

/**
 * Get the content pane where AfComponent children of this
 * AfComponent will be added.
 * In most cases the content pane will be this AfComponent itself,
 * but in AfPanelGroupLayout it is a separate sprite.  
 * 
 * @return the shape containing AfComponent children of this component
 *         
 */
DvtAfPanelGroupLayout.prototype.getContentShape = function() {
  return this._contentPane;
};


DvtAfPanelGroupLayout.prototype.renderSelfBeforeChildren = function() {
  DvtAfPanelGroupLayout.superclass.renderSelfBeforeChildren.call(this);

  //whole client area is available to children, but will
  //be reduced as children are rendered
  this._acw = this._cw;
  this._ach = this._ch;

};

DvtAfPanelGroupLayout.prototype.renderChild = function(child) {
  if (child._render) {
    child._render(this._acw, this._ach);
  }
  else if (child.render) {
    //Performance: using the same context during rendering
    //so we have a list of dimensions for all text objects
    var context = this._context;
    DvtAfComponent._renderComp(context, child, this._acw, this._ach);
  }

  var dim = DvtAfComponent.getDimensions(child);

  //adjust the remaining size of the client area available to 
  //children based on the size of the child just rendered
  var bVerticalLayout = (this.getLayout() == DvtAfPanelGroupLayout.LAYOUT_VERTICAL);
  if (bVerticalLayout) {
    this._ach -= dim.h;
  }
  else {
    this._acw -= dim.w;
  }
};

/**
 * Perform layout of this display object.
 * @override
 */
DvtAfPanelGroupLayout.prototype.doLayout = function() {

  var context = this._context;
  if (this.getLayout() == DvtAfPanelGroupLayout.LAYOUT_VERTICAL) {
    DvtAfComponentUtils.doVerticalLayout(context, this);
  }
  else {
    DvtAfComponentUtils.doHorizontalLayout(context, this);
  }
};



DvtAfPanelGroupLayout.prototype.getTagName = function() {
  return DvtAfPanelGroupLayout.TAG_NAME;
}


/**
 * DvtAfPanelFormLayout
 * Available client size
 *  _acw 
 *  _ach
 *
 */
var DvtAfPanelFormLayout = function() {
  this.Init();
};


/*
 * make DvtAfPanelFormLayout a subclass of DvtAfComponent
 */
DvtObj.createSubclass(DvtAfPanelFormLayout, DvtAfComponent, "DvtAfPanelFormLayout");

/*
 * Register the AfPanelFormLayout tag with DvtAfComponentFactory
 */
DvtAfPanelFormLayout.TAG_NAME = "pfl";
DvtAfComponentFactory.registerComponent("dvt", DvtAfPanelFormLayout.TAG_NAME, 
                                        new DvtAfTagHandler(DvtAfPanelFormLayout));

// AfPanelFormLayout Attributes



/*-------------------------------------------------------------------------*/
/*   AfComponent attributes                                                */
/*-------------------------------------------------------------------------*/


/**
 * @private
 * Note: only need to create one instance of _afStyles per class
 */
DvtAfPanelFormLayout.prototype.GetDefaultStyles = function() {
  return DvtAfPanelFormLayout._afStyles;
};

/**
 * @private
 * Note: only need to create one instance of _afStyles per class
 */
DvtAfPanelFormLayout.prototype.SetDefaultStyles = function(styleList) {
  DvtAfPanelFormLayout._afStyles = styleList;
};


//Note: this method doesn't clone the style, caller must clone if modify
DvtAfPanelFormLayout.prototype._getContentCellStyle = function() {
  if (DvtAfPanelFormLayout._afStyles.length > 1)
    return DvtAfPanelFormLayout._afStyles[1];

  return DvtAfPanelFormLayout._afStyles[0];
};


//Note: this method doesn't clone the style, caller must clone if modify
DvtAfPanelFormLayout.prototype._getLabelCellStyle = function() {
  if (DvtAfPanelFormLayout._afStyles.length > 2)
    return DvtAfPanelFormLayout._afStyles[2];

  return DvtAfPanelFormLayout._afStyles[0];
};
    


/*-------------------------------------------------------------------------*/
/*   CreateDisplayObject                                                   */
/*-------------------------------------------------------------------------*/
DvtAfPanelFormLayout.prototype.createDisplayObj = function(context) {
  var dispObj = DvtAfPanelFormLayout.superclass.createDisplayObj.call(this, context);

  this.addChildDisplayObjs(context, this._contentPane);

  return dispObj;
};


/*-------------------------------------------------------------------------*/
/*   Create Content Shape                                                  */
/*-------------------------------------------------------------------------*/
DvtAfPanelFormLayout.prototype.createContentShape = function(icontext) {
  this._contentPane = new DvtRect(icontext, 0, 0, 0, 0, this._getUniqueId() + "_cp");

  var style = this.getCSSStyle();
  this._contentPane.setCSSStyle(style);

  return this._contentPane;
};


/**
 * Get the content pane where AfComponent children of this
 * AfComponent will be added.
 * In most cases the content pane will be this AfComponent itself,
 * but in AfPanelFormLayout it is a separate sprite.  
 * 
 * @return the shape containing AfComponent children of this component
 *         
 */
DvtAfPanelFormLayout.prototype.getContentShape = function() {
  return this._contentPane;
};


DvtAfPanelFormLayout.prototype.renderSelfBeforeChildren = function() {
  DvtAfPanelFormLayout.superclass.renderSelfBeforeChildren.call(this);

  //whole client area is available to children, but will
  //be reduced as children are rendered
  this._acw = this._cw;
  this._ach = this._ch;

};

/**
 * @override
 * Override to determine child panelLabelAndMessage with
 * longest label, for layout and rendering purposes.
 */
DvtAfPanelFormLayout.prototype.renderChildren = function() {
  var child;
  var maxLabelWidth = 0;
  var cnt = this.getNumChildren();

  for (var i = 0; i < cnt; i++) {
    child = this.getChildAt(i);

    //figure out the longest label, so that we can render the
    //other panelLabelAndMessage children with reduced widths
    //in anticipation of them being shifted over to align
    //with the longest label
    if (child && child instanceof DvtAfPanelLabelAndMessage) {
      child.MergeUnderLabelStyle(this._getLabelCellStyle());
      child.MergeUnderContentStyle(this._getContentCellStyle());

      var dim = child.getLabelDimensions();
      maxLabelWidth = Math.max(dim.w, maxLabelWidth);
    }
  }

  this._maxLabelWidth = maxLabelWidth;

  DvtAfPanelFormLayout.superclass.renderChildren.call(this);
};

/**
 * @override
 */
DvtAfPanelFormLayout.prototype.renderChild = function(child) {

  //reduce the width available to children panelLabelAndMessages based
  //on the label width and the longest label width in anticipation of them
  //being shifted over to align with the longest label
  var labelWidthDiff = 0;
  var labelStyle = this._getContentCellStyle();
  var dim;

  if (child instanceof DvtAfPanelLabelAndMessage) {
    dim = child.getLabelDimensions();
    labelWidthDiff = this._maxLabelWidth - dim.w;

    if (child._getLabelStyle())
      labelStyle = child._getLabelStyle();

    child._setMaxLabelWidth(this._maxLabelWidth);
  }

  // calculate margin, border and padding
  var marginTop = labelStyle.getMarginTop();
  var marginLeft = labelStyle.getMarginLeft();
  var marginRight = labelStyle.getMarginRight();
  var marginBottom = labelStyle.getMarginBottom();

  var bdTopWidth = labelStyle.getBorderTopWidth();
  var bdLeftWidth = labelStyle.getBorderLeftWidth();
  var bdRightWidth = labelStyle.getBorderRightWidth();
  var bdBottomWidth = labelStyle.getBorderBottomWidth();

  var paddingTop = labelStyle.getPaddingTop();
  var paddingLeft = labelStyle.getPaddingLeft();
  var paddingRight = labelStyle.getPaddingRight();
  var paddingBottom = labelStyle.getPaddingBottom();

  var ww = this._acw - labelWidthDiff - paddingLeft - paddingRight;
  if (child._render) {
    child._render(ww, this._ach);
  }
  else if (child.render) {
    //Performance: using the same context during rendering
    //so we have a list of dimensions for all text objects
    DvtAfComponent._renderComp(this._context, child, ww, this._ach);
  }

  //adjust the remaining size of the client area available to 
  //children based on the size of the child just rendered
  dim = DvtAfComponent.getDimensions(child);
  this._ach -= dim.h - paddingTop - paddingBottom;
};

/**
 * Perform layout of this display object.
 * @override
 */
DvtAfPanelFormLayout.prototype.doLayout = function() {
  var context = this._context;
  var arDim = [];
  var cnt = this.getNumChildren();
  var child;
  var contentX = 0;
  var maxContentX = 0;

  //for BiDi, make first pass at layout as if it was L2R, and then at end make second pass
  //to adjust for R2L
  for (var i = 0; i < cnt; i++) {
    child = this.getChildAt(i);

    if (! child.renderMe || child.renderMe() && 
        child instanceof DvtAfPanelLabelAndMessage) {

      var dim = DvtAfComponent.getDimensions(child);
      arDim.push(dim);
      maxContentX = Math.max(maxContentX, child._getContentX());
    }
  }

  var labelStyle;
  var maxContentExtent = 0;
  var paddingTop = 0;
  var paddingBottom = 0;
  var paddingLeft = 0;
  var paddingRight = 0;
  var dim;
  var currY = this._cy;

  for (var i = 0; i < cnt; i++) {
    child = this.getChildAt(i);
    labelStyle = this._getContentCellStyle();
    dim = arDim[i];

    if (child instanceof DvtAfPanelLabelAndMessage) {
      if (child._getLabelStyle()) {
        labelStyle = child._getLabelStyle();
      }
      if (labelStyle) {
        paddingTop = labelStyle.getPaddingTop();
        paddingBottom = labelStyle.getPaddingBottom();
      }

      //for R2L locale, pad right side of label
      if (DvtStyleUtils.isLocaleR2L()) {
        var textAlignRight = true;
        if (labelStyle) {
          paddingRight = labelStyle.getPaddingRight();
          if (labelStyle.getStyle(DvtCSSStyle.TEXT_ALIGN) == "left")
            textAlignRight = false;
        }
        //Note: textField was padded with extra 2px on each side
//         childx = contentX + paddingRight - 2;
        childx = contentX + paddingRight;
        if (! textAlignRight) {
          childx += (maxContentX - child._getContentX());
        }
      }
      //for L2R locale, pad left side of label
      else {
        var textAlignLeft = true;
        if (labelStyle) {
          paddingLeft = labelStyle.getPaddingLeft();
          if (labelStyle.getStyle(DvtCSSStyle.TEXT_ALIGN) == "right")
            textAlignLeft = false;
        }
        //Note: textField was padded with extra 2px on each side
//         childx = contentX + paddingLeft - 2;
        childx = contentX + paddingLeft;
        if (! textAlignLeft) {
          childx += (maxContentX - child._getContentX());
        }
      }

//       childy = currY + paddingTop - 2;
//       currY += dim.h + paddingBottom - 2;
      childy = currY + paddingTop;
      currY += dim.h + paddingBottom;
          
      //calculate max coord of right side of content
      if (childx + dim.w > maxContentExtent)
        maxContentExtent = childx + dim.w;

      // set group translate x and y
      DvtAfComponent._setTranslateGroup(child, childx, childy);
    }
    else if (child instanceof DvtAfComponent) {
      //for R2L locale, pad right side of label
      if (DvtStyleUtils.isLocaleR2L()) {
//         childx = currX + paddingRight - 2;
        childx = currX + paddingRight;
      }
      //for L2R locale, pad left side of label
      else {
//         childx = currX + paddingLeft - 2;
        childx = currX + paddingLeft;
      }

//       childy = currY + paddingTop - 2;
      childy = currY + paddingTop;

      //TODO: cannot -2 here unless it is textField
      currY += dim.h + paddingBottom;

      //calculate max coord of right side of content
      if (childx + dim.w > maxContentExtent)
        maxContentExtent = childx + dim.w;

      // set group translate x and y
      DvtAfComponent._setTranslateGroup(child, childx, childy);
    }
  }

  //re-layout for right-to-left locale
  if (DvtStyleUtils.isLocaleR2L()) {
    //if an explicit width is set, or if the content is wider than the client width,
    //then start laying out at right side of client area
    if (this._ew > -1 || maxContentExtent > this._cx + this._cw) {
      contentX = this._cx + this._cw;
    }
    //otherwise, start laying out at right side of content width
    else {
      contentX = maxContentExtent;        
    }
    for (var i = 0; i < cnt; i++) {
      child = this.getChildAt(i);

      if (! child.renderMe || child.renderMe()) {
        dim = arDim[i];
        //content is currently laid out as if locale were L2R, so we can just
        //take the offsets for the left side of the content from the left edge
        //of the panelFormLayout and set them as the offsets for the right side
        //of the content from the right edge of the panelFormLayout for BiDi
        childx = contentX - (childx - this._cx) - dim.w;

        child.setTranslateX(childx);
      }
    }
  }

  dim = this.getContentShape().getDimensions();
  this._cw = this.getCalcWidth(dim.w);
  this._ch = this.getCalcHeight(dim.h);
};


/**
 * @override
 */
DvtAfPanelFormLayout.prototype.ResolveEm = function() {
  DvtAfPanelFormLayout.superclass.ResolveEm.call(this);

  if (! this._contentCellStyle) {
    this._contentCellStyle = DvtAfPanelFormLayout._afStyles[1].clone();
  }
  this.resolveEmUnit(this._contentCellStyle, this);

  if (! this._labelCellStyle) {
    this._labelCellStyle = DvtAfPanelFormLayout._afStyles[2].clone();
  }
  this.resolveEmUnit(this._labelCellStyle, this);
};



DvtAfPanelFormLayout.prototype.getTagName = function() {
  return DvtAfPanelFormLayout.TAG_NAME;
};





/*
    protected function getSizeAfterLayout():Rectangle
    {
      //add m_clientX and m_clientY to account for right and bottom padding
      //around children
      var iWidth = width + m_clientX;
      var iHeight = height + m_clientY;
      
      //constrain by available size
      if (iWidth > m_availWidth)
        iWidth = m_availWidth;
      if (iHeight > m_availHeight)
        iHeight = m_availHeight;
      
      //use explicit sizes if set
      if (m_explicitWidth > -1)
        iWidth = m_explicitWidth;
      if (m_explicitHeight > -1)
        iHeight = m_explicitHeight;
      
      return new Rectangle(0, 0, iWidth, iHeight);
    }
    
    override protected function getBackgroundImageRect():Rectangle{
    	var r:Rectangle = getSizeAfterLayout();
    	return new Rectangle(m_clientX, m_clientY, width - m_leftWidth - m_rightWidth, height - m_bottomHeight - m_topHeight);
    }
    
    override protected function renderSelfAfterLayout():void
    {
      var newSize:Rectangle = getSizeAfterLayout();
      drawRect(0, 0, newSize.width, newSize.height);
      
      //comment out scrollRect in order to support elliptical panelCard:
      //set the scrollrect so that content is cropped to the size of this container
      //scrollRect = new Rectangle(0, 0, newSize.width, newSize.height);
    }

*/
/**
 * DvtAfPanelLabelAndMessage
 * Available client size
 *  _acw 
 *  _ach
 *
 */
var DvtAfPanelLabelAndMessage = function() {
  this.Init();
};


/*
 * make DvtAfPanelLabelAndMessage a subclass of DvtAfComponent
 */
DvtObj.createSubclass(DvtAfPanelLabelAndMessage, DvtAfComponent, "DvtAfPanelLabelAndMessage");

/*
 * Register the AfPanelLabelAndMessage tag with DvtAfComponentFactory
 */
DvtAfPanelLabelAndMessage.TAG_NAME = "plm";
DvtAfComponentFactory.registerComponent("dvt", DvtAfPanelLabelAndMessage.TAG_NAME, 
                                        new DvtAfTagHandler(DvtAfPanelLabelAndMessage));

// AfPanelLabelAndMessage Attributes
DvtAfPanelLabelAndMessage.ATTR_LABEL = "label";
DvtAfPanelLabelAndMessage.ATTR_LABEL_STYLE = "labelStyle";


DvtAfPanelLabelAndMessage._HPADDING = 5;


/*-------------------------------------------------------------------------*/
/*   AfComponent attributes                                                */
/*-------------------------------------------------------------------------*/
DvtAfPanelLabelAndMessage.prototype.getLabel = function() {
  return this.getStringProp(DvtAfPanelLabelAndMessage.ATTR_LABEL);
};

DvtAfPanelLabelAndMessage.prototype.setLabel = function(label) {
  this.setProperty(DvtAfPanelLabelAndMessage.ATTR_LABEL, label);
};

/**
 * Get the inline styles for the label portion of the component.
 * @return {string} label style
 */
DvtAfPanelLabelAndMessage.prototype.getLabelStyle = function() {
  return this.getStringProp(DvtAfPanelLabelAndMessage.ATTR_LABEL_STYLE);
};

/**
 * Set the inline styles for the label portion of the component.
 * @params {string} labelStyle 
 */
DvtAfPanelLabelAndMessage.prototype.setLabelStyle = function(labelStyle) {
  // get the default label style
  var labelCSSStyle = this._getDefaultLabelCSSStyle().clone();

  // set label style on top
  labelCSSStyle.parseInlineStyle(labelStyle);

  // set the label css style
  this._setLabelStyle(labelCSSStyle);

  //TODO: save labelStyle or labelCSSStyle.toString()?
  this.setProperty(DvtAfPanelLabelAndMessage.ATTR_LABEL_STYLE, labelCSSStyle.toString());
};


/**
 * @private
 * Note: only need to create one instance of _afStyles per class
 */
DvtAfPanelLabelAndMessage.prototype.GetDefaultStyles = function() {
  return DvtAfPanelLabelAndMessage._afStyles;
};

/**
 * @private
 * @params {array} styleList is an array of DvtCSSStyle
 * Note: only need to create one instance of _afStyles per class
 */
DvtAfPanelLabelAndMessage.prototype.SetDefaultStyles = function(styleList) {
  DvtAfPanelLabelAndMessage._afStyles = styleList;

  //label style: styleList[1]
  //content cell style: styleList[2]
};



/*-------------------------------------------------------------------------*/
/*   CreateDisplayObject                                                   */
/*-------------------------------------------------------------------------*/
DvtAfPanelLabelAndMessage.prototype.createDisplayObj = function(context) {
  var dispObj = DvtAfPanelLabelAndMessage.superclass.createDisplayObj.call(this, context);

  this.addChildDisplayObjs(context, this._contentPane);

  return dispObj;
};


/*-------------------------------------------------------------------------*/
/*   Create Content Shape                                                  */
/*-------------------------------------------------------------------------*/
DvtAfPanelLabelAndMessage.prototype.createContentShape = function(icontext) {
  this._contentPane = new DvtRect(icontext, 0, 0, 0, 0, this._getUniqueId() + "_cp");

  this._maxLabelWidth = 0;
  var style = this.getCSSStyle();
  this._contentPane.setCSSStyle(style);

  var label = this.getLabel();
  if (label) {

  // create a DvtAfOutputText component
//     var labelComp = new DvtAfOutputText();
//     labelComp._context = this._context;
//     labelComp.setId(this._getUniqueId() + "_lb");
//     labelComp.setValue(label);
//     this.addChildToFront(labelComp);
//     var labelStyle = this._getLabelStyle();
//     this._labelField = labelComp.createTextShape(false, label, labelStyle, 
//                                             this._getDefaultLabelCSSStyle());

  // create just a DvtText instead
    var labelStyle = this._getLabelStyle();
    //can label and PLAM have the same id?
    this._labelField = this.createTextShape(false, label, labelStyle, 
                                            this._getDefaultLabelCSSStyle());
    this._contentPane.addChild(this._labelField);
  }

  return this._contentPane;
};

/**
 * Get the content pane where AfComponent children of this
 * AfComponent will be added.
 * In most cases the content pane will be this AfComponent itself,
 * but in AfPanelLabelAndMessage it is a separate sprite.  
 * 
 * @return the shape containing AfComponent children of this component
 *         
 */
DvtAfPanelLabelAndMessage.prototype.getContentShape = function() {
  return this._contentPane;
};

// return label's displayable (text shape)
DvtAfPanelLabelAndMessage.prototype._getLabelField = function() {
  return this._labelField;
};


//see DvtStyleUtils x15
DvtAfPanelLabelAndMessage.prototype._getDefaultLabelCSSStyle = function() {
  if (DvtAfPanelLabelAndMessage._afStyles.length > 1) {
    return DvtAfPanelLabelAndMessage._afStyles[1];
  }
  // should never return a null
  return null;
}


// Similiar to getLabelStyle but returns a DvtCSSStyle instead of a String
DvtAfPanelLabelAndMessage.prototype._getLabelStyle = function() {
  if (! this._labelStyle) {
    var style = this._getDefaultLabelCSSStyle().clone();
    var inlineStyle = this.getLabelStyle();
    if (inlineStyle) {
      style.parseInlineStyle(inlineStyle);
    }
    this._labelStyle = style;
  }

  return this._labelStyle;
};

// Similiar to setLabelStyle but pass in a DvtCSSStyle instead of a String
DvtAfPanelLabelAndMessage.prototype._setLabelStyle = function(labelStyle) {
  this._labelStyle = labelStyle;
};


DvtAfPanelLabelAndMessage.prototype._getContentCellStyle = function() {
  return this._contentCellStyle;
};


// af contentCellStyle + cssStyle 
DvtAfPanelLabelAndMessage.prototype._getContentStyle = function() {
  if (! this._contentStyle) {
    this._contentStyle = this._getContentCellStyle().clone();
    this._contentStyle.merge(this.getCSSStyle());
  }
  return this._contentStyle;
};

DvtAfPanelLabelAndMessage.prototype.MergeUnderContentStyle = function(style) {
  var contentStyle = this._getContentStyle();
  contentStyle.mergeUnder(style);
};

DvtAfPanelLabelAndMessage.prototype.MergeUnderLabelStyle = function(style) {
  var labelStyle = this._getLabelStyle();
  labelStyle.mergeUnder(style);
};


DvtAfPanelLabelAndMessage.prototype.ResolveEm = function() {
  DvtAfPanelLabelAndMessage.superclass.ResolveEm.call(this);

  if (! this._contentCellStyle) {
    this._contentCellStyle = DvtAfPanelLabelAndMessage._afStyles[2].clone();
  }
  this.resolveEmUnit(this._contentCellStyle, this);

  if (this._labelStyle != null) {
    this.resolveEmUnit(this._labelStyle, this);
  }
};

DvtAfPanelLabelAndMessage.prototype._getContentX = function() {
  //this is set during doLayout() of panelFormLayout
  return this._cx;
};

/**
 * @private
 * @params {int} maxWidth
 */
DvtAfPanelLabelAndMessage.prototype._setMaxLabelWidth = function(maxWidth) {
  //this is set during doLayout() of panelFormLayout
  this._maxLabelWidth = maxWidth;
};

/**
 * Calculate the width of the label part of the component.  
 * 
 * @return width of label
 */
DvtAfPanelLabelAndMessage.prototype.getLabelDimensions = function() {
  //this function should draw the label the same
  //as renderSelfBeforeChildren() to calculate the
  //width, but without rendering to the display
  if (! this._labelDim) {
    var label = this._getLabelField();
    this._labelDim = label ? label.getDimensions() : new DvtRectangle(0, 0, 0, 0);
  }

  return this._labelDim;
};


DvtAfPanelLabelAndMessage.prototype.renderSelfBeforeChildren = function() {
  DvtAfPanelLabelAndMessage.superclass.renderSelfBeforeChildren.call(this);

  //whole client area is available to children, but will
  //be reduced as children are rendered
  this._acw = this._cw;
  this._ach = this._ch;

  // add label
  var labelField = this._getLabelField();

  labelField.setMaxWidth(this._acw);
  labelField.setX(this.getCX());
  labelField.setY(this.getCY());

  var cellStyle;
  //for right-to-left locale, use padding from the right side of the content
  if (DvtStyleUtils.isLocaleR2L()) {
    cellStyle = this._getContentCellStyle();
  }
  //for left-to-right locale, use padding from the right side of the label
  else {
    cellStyle = this._getLabelStyle();
  }

  var paddingRight;
  if (cellStyle && cellStyle.getPaddingRight()) {
    paddingRight = cellStyle.getPaddingRight();
  }
  else {
    paddingRight = DvtAfPanelLabelAndMessage._HPADDING;
  }

  this._acw -= (this.getLabelDimensions().w + paddingRight);        

  var child;
  for (var i = 0; i < this.getNumChildren(); i++) {
    child = this.getChildAt(i);
    if (child instanceof DvtAfComponent) {
      var style = child.getCSSStyle().clone();
      style.mergeUnder(this._getContentStyle());
    }
  }

};

DvtAfPanelLabelAndMessage.prototype.renderChild = function(child) {
  if (child._render) {
    child._render(this._acw, this._ach);
  }
  else if (child.render) {
    //Performance: using the same context during rendering
    //so we have a list of dimensions for all text objects
    var context = this._context;
    DvtAfComponent._renderComp(context, child, this._acw, this._ach);
  }

  var dim = DvtAfComponent.getDimensions(child);

  //adjust the remaining size of the client area available to 
  //children based on the size of the child just rendered
  this._acw -= dim.w;

};

/**
 * Perform layout of this display object.
 * @override
 */
DvtAfPanelLabelAndMessage.prototype.doLayout = function() {

  var context = this._context;
  var lbDim = this.getLabelDimensions();
  var maxChildHeight = lbDim.h;

  var arDim = [];
  var afRoot = this;
  var cnt = afRoot.getNumChildren();
  var child;

  //for BiDi, make first pass at layout as if it was L2R, and then at end make second pass
  //to swap order of label and contents
  for (var i = 0; i < cnt; i++) {
    child = afRoot.getChildAt(i);

    if (! child.renderMe || child.renderMe()) {
      var dim = DvtAfComponent.getDimensions(child);
      maxChildHeight = Math.max(maxChildHeight, dim.h);
      arDim.push(dim);
    }
  }

//   var afRootDim = DvtAfComponentUtils._calcHContentSize(context, afRoot, arDim);
//   afRootDim.h = Math.max(afRootDim.h, lbDim.h);
//   var maxChildHeight = this.getCalcHeight(afRootDim.h);

  var cellStyle;
  //for right-to-left locale, use padding from the right side of the content
  if (DvtStyleUtils.isLocaleR2L()) {
    cellStyle = this._getContentCellStyle();
  }
  //for left-to-right locale, use padding from the right side of the label
  else {
    cellStyle = this._getLabelStyle();
  }

  var paddingRight;
  if (cellStyle && cellStyle.getPaddingRight()) {
    paddingRight = cellStyle.getPaddingRight();
  }
  else {
    paddingRight = DvtAfPanelLabelAndMessage._HPADDING;
  }

  //BiDi: need to calculate labelWidth based on textAlign and reading direction
  var labelWidth; 
  var textAlign = this._getLabelStyle().getStyle(DvtCSSStyle.TEXT_ALIGN);

  if (DvtStyleUtils.isLocaleR2L()) {
    labelWidth = (textAlign === "left") ?
      lbDim.w : Math.max(lbDim.w, this._maxLabelWidth);
  }
  else {
    labelWidth = (textAlign === "right") ?
      lbDim.w : Math.max(lbDim.w, this._maxLabelWidth);
  }

  var vAlign = this.getStyle(DvtCSSStyle.VERTICAL_ALIGN);
  if (! vAlign) {
    vAlign = DvtAfComponent.VALIGN_TOP;
  }

  var contentX = this._cx;
  var contentY = this._cy;

  this._cx = contentX + labelWidth + paddingRight;
  var currX = this._cx;

  for (var i = 0; i < cnt; i++) {
    if (! arDim[i])
      continue;

    child = afRoot.getChildAt(i);

    if (! child.renderMe || child.renderMe()) {

      var cw = arDim[i].w;
      var ch = arDim[i].h;

      if (vAlign == DvtAfComponent.VALIGN_TOP) {
        childy = contentY;
      }
      else if (vAlign == DvtAfComponent.VALIGN_MIDDLE) {
        childy = contentY + (afRoot._ch - ch) / 2;
      }
      else if (vAlign == DvtAfComponent.VALIGN_BOTTOM) {
        childy = contentY + afRoot._ch - ch;
      }

      childx = currX;
      currX += cw;

      // set group translate x and y
      DvtAfComponent._setTranslateGroup(child, childx, childy);
    }
  }

  var labelField = this._getLabelField();

  //BiDi: re-layout for right-to-left locale
  if (DvtStyleUtils.isLocaleR2L()) {
    //currX should now be rightmost extent of contents, so adjust left
    //by width of label to position label
    labelField.setX(currX - lbDim.w);

    //set currX to be mirror of itself, so that it is offset from right side
    //instead of left side
    currX = currX + contentX - this._cx;

    for (var i = 0; i < cnt; i++) {
      child = this.getChildAt(i);
      if (! child.renderMe || child.renderMe()) {

        //subtract width of component first, because currX is currently
        //set to right side of component
        currX -= arDim[i].w;
        childx = currX;
      }

      //TODO?? set translate x
      child.setTranslateX(childx);
    }
  }

  //TODO: textField is padded with 2px on all 4 sides
  sVAlign = this._getLabelStyle().getStyle(DvtCSSStyle.VERTICAL_ALIGN);
  if (sVAlign == null) {
    sVAlign = DvtAfComponent.VALIGN_MIDDLE;
  }
  if (sVAlign == DvtAfComponent.VALIGN_MIDDLE) {
    labelField.setY(contentY + (maxChildHeight - lbDim.h)/2);
  } 
  else if (sVAlign == DvtAfComponent.VALIGN_BOTTOM) {
    labelField.setY(contentY + maxChildHeight - lbDim.h);
  }

  var dim = this.getContentShape().getDimensions();
  this._cw = this.getCalcWidth(dim.w);
  this._ch = this.getCalcHeight(dim.h);
};


DvtAfPanelLabelAndMessage.prototype.getTagName = function() {
  return DvtAfPanelLabelAndMessage.TAG_NAME;
};






/*
    override protected function renderSelfAfterLayout() {
      drawTextBox();
    }
    
    override protected function getBackgroundImageRect() {
      return new DvtRectangle(this._clientX, this._clientY, 
      width - this._leftWidth - this._rightWidth, 
      height - this._topHeight - this._bottomHeight);
    }
*/



/**
 * DvtAfSpacer
 */
var DvtAfSpacer = function() {
  this.Init();
};

/**
 * make DvtAfSpacer a subclass of DvtAfComponent
 */
DvtObj.createSubclass(DvtAfSpacer, DvtAfComponent, "DvtAfSpacer");

/*
 * Register the AfSpacer tag with DvtAfComponentFactory
 */
DvtAfSpacer.TAG_NAME = "sp";
DvtAfComponentFactory.registerComponent("dvt", DvtAfSpacer.TAG_NAME, 
                                        new DvtAfTagHandler(DvtAfSpacer));

// AfSpacer Attributes
DvtAfSpacer.ATTR_WIDTH = "width";
DvtAfSpacer.ATTR_HEIGHT = "height";


/*-------------------------------------------------------------------------*/
/*   AfComponent attributes                                                */
/*-------------------------------------------------------------------------*/
DvtAfSpacer.prototype.setWidth = function(width) {
  this.setProperty(DvtAfSpacer.ATTR_WIDTH, width);
};

DvtAfSpacer.prototype.getWidth = function() {
  return this.getProperty(DvtAfSpacer.ATTR_WIDTH);
};

DvtAfSpacer.prototype.setHeight = function(height) {
  this.setProperty(DvtAfSpacer.ATTR_HEIGHT, height);
};

DvtAfSpacer.prototype.getHeight = function() {
  return this.getProperty(DvtAfSpacer.ATTR_HEIGHT);
};

/**
 * @private
 * Note: only need to create one instance of _afStyles per class
 */
DvtAfSpacer.prototype.GetDefaultStyles = function() {
  return DvtAfSpacer._afStyles;
};

/**
 * @private
 * Note: only need to create one instance of _afStyles per class
 */
DvtAfSpacer.prototype.SetDefaultStyles = function(styleList) {
  DvtAfSpacer._afStyles = styleList;
};


/*-------------------------------------------------------------------------*/
/*   CreateDisplayObject                                                   */
/*-------------------------------------------------------------------------*/
/*
DvtAfSpacer.prototype.createDisplayObj = function(context) {
  this._context = context;
  var dispObj = new DvtRect(context.getContext(), 0, 0, 
                            this.getWidth(), this.getHeight(), this.getId());

  this._bgShape = dispObj;
  return dispObj;
};
*/

DvtAfSpacer.prototype.adjustClientArea = function(cssStyle) {
  DvtAfSpacer.superclass.adjustClientArea.call(this, cssStyle);

  //Bug 13420898 - got invalid value error when spacer with unspecified width
  if (this._cw === Number.MAX_VALUE)
    this._cw = 1;
  else
    this._cw = this._aw;

  if (this._ch === Number.MAX_VALUE)
    this._ch = 1;
  else
    this._ch = this._ah;

};

DvtAfSpacer.prototype.renderSelfBeforeChildren = function() {
  DvtAfSpacer.superclass.renderSelfBeforeChildren.call(this);
};

DvtAfSpacer.prototype.getTagName = function() {
  return DvtAfSpacer.TAG_NAME;
}


// DvtAfSpacer.prototype._hasBgShape = function() {
//   return DvtAfComponent._CREATE_BG_SHAPE;
// }

/**
 * DvtAfSeparator
 */
var DvtAfSeparator = function() {
  this.Init();
};

/*
 * make DvtAfSeparator a subclass of DvtAfComponent
 */
DvtObj.createSubclass(DvtAfSeparator, DvtAfComponent, "DvtAfSeparator");

/*
 * Register the AfSeparator tag with DvtAfComponentFactory
 */
DvtAfSeparator.TAG_NAME = "se";
DvtAfComponentFactory.registerComponent("dvt", DvtAfSeparator.TAG_NAME, 
                                        new DvtAfTagHandler(DvtAfSeparator));


/*-------------------------------------------------------------------------*/
/*   AfComponent attributes                                                */
/*-------------------------------------------------------------------------*/
/**
 * @private
 * Note: only need to create one instance of _afStyles per class
 */
DvtAfSeparator.prototype.GetDefaultStyles = function() {
  return DvtAfSeparator._afStyles;
};

/**
 * @private
 * Note: only need to create one instance of _afStyles per class
 */
DvtAfSeparator.prototype.SetDefaultStyles = function(styleList) {
  DvtAfSeparator._afStyles = styleList;
};


/*-------------------------------------------------------------------------*/
/*   Create Content Shape                                                  */
/*-------------------------------------------------------------------------*/
DvtAfSeparator.prototype.createContentShape = function(icontext) {
  // create a container for separator
  var id = this.getId();
  if (! id) {
    id = this._getUniqueId();
  }
  var group = new DvtContainer(icontext, id);

  var line1 = new DvtLine(icontext, 0, 0, 1, 0, id + "_1");
  var line2 = new DvtLine(icontext, 0, 1, 1, 1, id + "_2");
  this._line1 = line1;
  this._line2 = line2;

  var style = this.getCSSStyle();
  var color1 = style[DvtCSSStyle.BORDER_TOP_COLOR];
  var color2 = style[DvtCSSStyle.BORDER_BOTTOM_COLOR];

  if (! color1) {
    color1 = "#ADB6C7";
  }
  if (! color2) {
    color2 = "#FFFFFF";
  }
  var stroke1 = new DvtSolidStroke(color1, 1, 1);
  line1.setStroke(stroke1);

  var stroke2 = new DvtSolidStroke(color2, 1, 1);
  line2.setStroke(stroke2);

  group.addChild(line1);
  group.addChild(line2);

  return group;
};


/**
 * @override
 */
DvtAfSeparator.prototype.renderSelfBeforeChildren = function() {
  DvtAfSeparator.superclass.renderSelfBeforeChildren.call(this);

  var cy = this._cy;
  this._line1.setY1(cy);
  this._line1.setY2(cy);

  this._line2.setY1(cy);
  this._line2.setY2(cy);
};


/**
 * @protected
 * Return bounding rectangle
 */
DvtAfSeparator.prototype.getBounds = function() {
  this._ch = 0;
  return DvtAfSeparator.superclass.getBounds.call(this);
}

/**
 */
DvtAfSeparator.prototype.setMaxWidth = function(w) {
  var x1 = this._cx;
  var x2 = Math.max(0, w - this._cx);

  this._line1.setX1(x1);
  this._line1.setX2(x2);

  this._line2.setX1(x1);
  this._line2.setX2(x2);

};


DvtAfSeparator.prototype.getTagName = function() {
  return DvtAfSeparator.TAG_NAME;
}

DvtAfSeparator.prototype._hasBgShape = function() {
  return DvtAfComponent._CREATE_BG_SHAPE;
}

/**
 * DvtAfShowDetail
 */
var DvtAfShowDetail = function() {
  this.Init();
};

/*
 * make DvtAfShowDetail a subclass of DvtAfComponent
 */
DvtObj.createSubclass(DvtAfShowDetail, DvtAfComponent, "DvtAfShowDetail");

/*
 * Register the AfOutputText tag with DvtAfComponentFactory
 */
DvtAfShowDetail.TAG_NAME = "sd";
DvtAfComponentFactory.registerComponent("dvt", DvtAfShowDetail.TAG_NAME, 
                                        new DvtAfTagHandler(DvtAfShowDetail));


// AfOutputText Attributes
DvtAfShowDetail.ATTR_DISCLOSED = "disclosed";
DvtAfShowDetail.ATTR_DISCLOSED_TEXT = "disclosedText";
DvtAfShowDetail.ATTR_UNDISCLOSED_TEXT = "unDisclosedText";



/*-------------------------------------------------------------------------*/
/*   AfComponent attributes                                                */
/*-------------------------------------------------------------------------*/
DvtAfShowDetail.prototype.setDisclosed = function(disclosed) {
  this.setProperty(DvtAfShowDetail.ATTR_DISCLOSED, disclosed);
};

// default: wrapping
DvtAfShowDetail.prototype.isDisclosed = function() {
  return this.getBooleanProp(DvtAfShowDetail.ATTR_DISCLOSED, false);
};

DvtAfShowDetail.prototype.setDisclosedText = function(disclosedText) {
  this.setProperty(DvtAfShowDetail.ATTR_DISCLOSED_TEXT, disclosedText);
};

// default: no truncation
DvtAfShowDetail.prototype.getDisclosedText = function() {
  return this.getProperty(DvtAfShowDetail.ATTR_DISCLOSED_TEXT);
};

DvtAfShowDetail.prototype.setUnDisclosedText = function(unDisclosedText) {
  this.setProperty(DvtAfShowDetail.ATTR_UNDISCLOSED_TEXT, unDisclosedText);
};

// default: no truncation
DvtAfShowDetail.prototype.getUnDisclosedText = function() {
  return this.getProperty(DvtAfShowDetail.ATTR_UNDISCLOSED_TEXT);
};

DvtAfShowDetail.prototype.setExpandTooltip = function (tooltip) {
  this._expandTooltip = tooltip;
}

DvtAfShowDetail.prototype.getExpandTooltip = function () {
  return this._expandTooltip;
}

DvtAfShowDetail.prototype.setCollapseTooltip = function (tooltip) {
  this._collapseTooltip = tooltip;
}

DvtAfShowDetail.prototype.getCollapseTooltip = function () {
  return this._collapseTooltip;
}

/**
 * @private
 * Note: only need to create one instance of _afStyles per class
 */
DvtAfShowDetail.prototype.GetDefaultStyles = function() {
  return DvtAfShowDetail._afStyles;
};

/**
 * @private
 * Note: only need to create one instance of _afStyles per class
 */
DvtAfShowDetail.prototype.SetDefaultStyles = function(styleList) {
  DvtAfShowDetail._afStyles = styleList;
};

DvtAfShowDetail.prototype._getHeaderText = function () 
{
    return this.isDisclosed ? this.getDisclosedText() : this.getUnDisclosedText();
}

DvtAfShowDetail.prototype._getCollapsibleDialog = function ()
{
  return this._parent.getParent(); 
}

DvtAfShowDetail.prototype._toggle = function () {
  this.getTooltipManager().hideTooltip();

  var shape = this.getContentShape();
  var changeHeight = 0;
  var changeWidth = 0; 
  
  if (!this.isDisclosed()) {
    this.setDisclosed('true');
    shape._updateDisclosedState(this.isDisclosed(), this._getHeaderText());
    changeHeight = shape._childContainer.getDimensions().h;
  } else {
    changeHeight = -shape._childContainer.getDimensions().h;
    this.setDisclosed('false');
    shape._updateDisclosedState(this.isDisclosed(), this._getHeaderText());
  }

  this._ch += changeHeight;
  this._dim.h = this._ch;
  var container = this.getParent();
  var layoutContainer = null;
  while (container != null)
  {
    if (container.getTypeName() == 'DvtAfPanelGroupLayout')
        layoutContainer = container;
    
    container = container.getParent();
  }

  if (layoutContainer != null)
    layoutContainer.doLayout();

  var collapsiblePanel = this._getCollapsibleDialog();
  if (collapsiblePanel) {
      var dims = collapsiblePanel.getContentDimensions();
      dims.h = dims.h + changeHeight;
      dims.w = dims.w + changeWidth;
      collapsiblePanel.setContentDimensions(dims);
  }
}

/*-------------------------------------------------------------------------*/
/*   CreateDisplayObject                                                   */
/*-------------------------------------------------------------------------*/
DvtAfShowDetail.prototype.createDisplayObj = function(context) {
  var dispObj = DvtAfShowDetail.superclass.createDisplayObj.call(this, context);

  // create DvtAfShowDetail shape
  var shape = new DvtAfShowDetailDisp(context.getContext(), null, 0, 0, this.getId());

  // add to the background shape
  this.setContentShape(shape);
  this.addChildDisplayObjs(context, shape);
  
  return dispObj;
};

DvtAfShowDetail.prototype.addChildDisplayObjs = function(context, parentDO) {
  var child;
  var childDO;
  var cnt = this.getNumChildren();

  for (var i = 0; i < cnt; i++) {
    child = this.getChildAt(i);
    if (! child.renderMe || child.renderMe()) {
      childDO = child.createDisplayObj(context);
      if (childDO) {
        parentDO.addChild(childDO);
      }
    }
  }
};

DvtAfShowDetail.prototype.getImageTip = function () {
  var tooltip = null;
  if (!this.isDisclosed())
  {
      tooltip = this._collapseTooltip;
  } else
  {
      tooltip = this._expandTooltip;
  }

  if (tooltip == null)
      tooltip = this.getTooltipText();

  return tooltip;
}

DvtAfShowDetail.prototype.showTooltip = function (event) {
    var ttt = this.getImageTip();
    if (this.getImageTip() != null)
      this.getTooltipManager().showTooltip(event.pageX, event.pageY, 
                                           ttt, this.getContentShape()._headerImage, true);
}

DvtAfShowDetail.prototype.hideTooltip = function (event) {
  this.getTooltipManager().hideTooltip();
}

DvtAfShowDetail.prototype.renderSelfBeforeChildren = function() {
  DvtAfShowDetail.superclass.renderSelfBeforeChildren.call(this);
  var shape = this.getContentShape();
  if (shape.render)
  {
    shape.render(); // TODO RENDER FIX  probably just add if(shape.render) before calling render
  }
  shape._updateDisclosedState(this.isDisclosed(), this._getHeaderText());
  shape._headerImage.addEventListener('click', DvtObj.createCallback(this, this._toggle));
  shape._headerImage.addEventListener('mouseover', DvtObj.createCallback(this, this._handleMouseOver));
  shape._headerImage.addEventListener('mouseout', DvtObj.createCallback(this, this._handleMouseOut));
  shape._headerImage.addEventListener('mousedown', DvtObj.createCallback(this, this._handleMouseDown));

  this._acw = this._cw;
  this._ach = this._ch;

  var dim = shape._header.getDimensions();
  this._cw = dim.w;
  this._ch = dim.h;
};

DvtAfShowDetail.prototype.renderChild = function(child) {   
    child._render(this._acw, this._ach); 
};

DvtAfShowDetail.prototype.renderSelfAfterChildren = function() {
  DvtAfShowDetail.superclass.renderSelfAfterChildren.call(this);
  var shape = this.getContentShape();
  if (this.isDisclosed())
  {
      var childDim = shape._childContainer.getDimensions();
      this._ch += childDim.h;
  }
};


/**
 * @private
 * Transfer relevant attributes from the original tag to the shape element
 * @param {Object} shape  The shape element.
 */
DvtAfShowDetail.prototype._TransferAttributes = function(shape) {
  var shape = this.getContentShape();
  // shape._positionTopLeft(true);

  var cssStyle = this.createTextStyles(null, null);
  if (cssStyle) {
    shape.setCSSStyle(cssStyle);
  }

};

DvtAfShowDetail.prototype.getTagName = function() {
  return DvtAfShowDetail.TAG_NAME;
}

DvtAfShowDetail.prototype._handleMouseDown = function(event) {   
  this.getContentShape().updateState(DvtAfShowDetailDisp.DOWN);
};

DvtAfShowDetail.prototype._handleMouseOver = function(event) {   
  this.getContentShape().updateState(DvtAfShowDetailDisp.OVER);
  this.showTooltip(event)
};

DvtAfShowDetail.prototype._handleMouseOut = function(event) {   
  this.getContentShape().updateState(DvtAfShowDetailDisp.ENABLED);
  this.hideTooltip(event);
};

DvtAfShowDetail.prototype.setParentContainer = function(parent) {
  this._parent = parent;
}

/**
 */
var  DvtAfShowDetailDisp = function(context, id, x, y)
{   
   this.Init(context, id, x, y);
};

DvtObj.createSubclass(DvtAfShowDetailDisp, DvtContainer, "DvtAfShowDetailDisp");

DvtAfShowDetailDisp._ENA_COLOR = "#495D7C";
DvtAfShowDetailDisp._OVR_COLOR = "#245EDD";
DvtAfShowDetailDisp._DWN_COLOR = "#000000";
DvtAfShowDetailDisp._EXPAND_ARROW = 0; // right arrow
DvtAfShowDetailDisp._COLLAPSE_ARROW = 1; // down arrow
DvtAfShowDetailDisp.ENABLED = 0;
DvtAfShowDetailDisp.DOWN = 1;
DvtAfShowDetailDisp.OVER = 2;

DvtAfShowDetailDisp.prototype.Init = function(context, id, x, y)
{
  DvtAfShowDetailDisp.superclass.Init.call(this, context, id);  
  this.setTranslateX(x);
  this.setTranslateY(y);
  this._header = null;
  this._childContainer = null;
  this._disclosed = true;

  // ToDo: Create a function to generate subId for subComponent
  var header = new DvtContainer(this.getContext(), 'header');
  var childContainer = new DvtContainer(this.getContext(), 'childContainer');

  // ToDo: destroy header and childContainer on destroy
  this.addChild(header);
  this.addChild(childContainer);   

  this._header = header;
  this._childContainer = childContainer;
}

DvtAfShowDetailDisp.prototype.addChild = function(obj)
{
  if (this._childContainer != null)
  {
      this._childContainer.addChild(obj);
  } else
  {
      DvtAfShowDetailDisp.superclass.addChild.call(this, obj);  
  }
}

DvtAfShowDetailDisp.prototype.addChildAt = function(obj, index)
{
  if (this._childContainer != null)
  {
      this._childContainer.addChildAt(obj, index);
  } else
  {
      DvtAfShowDetailDisp.superclass.addChildAt.call(this, obj, index);  
  }
}

DvtAfShowDetailDisp.prototype._updateDisclosedState = function (disclose, text)
{
  if (this._headerImage) {
    if (disclose) {     
      this._headerImage.setPoints([3.0, 3.0, 11.0, 3.0, 7.0, 11.0]);
      this._childContainer.setVisible(true);
    } else {
      this._headerImage.setPoints([3.0, 3.0, 11.0, 7.0, 3.0, 11.0]);
      this._childContainer.setVisible(false);
    }

    if (this._headerText)
      this._headerText.setText(text);
  }
}

DvtAfShowDetailDisp.prototype._draw = function(color, type)
{
  if (this._header.getNumChildren() > 0)
      this._header.removeChildren();
      
  //TODO handle bidi
  switch(type) {
    case DvtAfShowDetailDisp._EXPAND_ARROW:
      this._headerImage = new DvtPolygon(this.getContext(), [3.0, 3.0, 11.0, 7.0, 3.0, 11.0]);
      break;
    default:
      this._headerImage = new DvtPolygon(this.getContext(), [3.0, 3.0, 11.0, 3.0, 7.0, 11.0]);
      break;
  }

  this._headerImage.setFill(new DvtSolidFill(color, 0.5));

//   ToDo: Find a better way to not cache headerImage
  this._header.addChild(this._headerImage);
  
  var headertext = new DvtText(this.getContext(), '');
  headertext.setCSSStyle(this.getCSSStyle());
  this._headerText = headertext;
  this._header.addChild(headertext); 
  headertext.setTranslateX(15);

  // ToDo: Let parent determine this
  this._childContainer.setVisible(true);
  this._childContainer.setTranslateX(15-DvtCommonLegend.DEFAULT_MARGINX);
  this._childContainer.setTranslateY(15);
}

DvtAfShowDetailDisp.prototype.render = function ()
{
   this._draw(DvtAfShowDetailDisp._ENA_COLOR, DvtAfShowDetailDisp._RIGHT_ARROW);
}

DvtAfShowDetailDisp.prototype.updateState = function(state) {
  switch(state) {
    case DvtAfShowDetailDisp.OVER:
      this._headerImage.setFill(new DvtSolidFill(DvtAfShowDetailDisp._OVR_COLOR, 0.5));
      break;
    case DvtAfShowDetailDisp.DOWN:
      this._headerImage.setFill(new DvtSolidFill(DvtAfShowDetailDisp._DWN_COLOR, 0.5));
      break;
    default:
      this._headerImage.setFill(new DvtSolidFill(DvtAfShowDetailDisp._ENA_COLOR, 0.5));
      break;
  }
}
// Copyright (c) 2008, 2012, Oracle and/or its affiliates. 
// All rights reserved. 
/*---------------------------------------------------------------------*/
/*   DvtCollapsiblePanel                                               */
/*   @creater -- Kevin Chow                                            */
/*---------------------------------------------------------------------*/
/**
 *  Creates a DvtCollapsiblePanel
 *  @class DvtCollapsiblePanel
 *  @extends DvtContainer
 *  @constructor
 *  @param {DvtContext} context The context object
 *  @param {id} The id of the CollapsiblePanel
 *  @param {x} The leftmost position of the CollapsiblePanel
 *  @param {y} The topmost position of the CollapsiblePanel
 *  @param {ww} The width of the CollapsiblePanel
 *  @param {hh} The height of the CollapsiblePanel
 */
var DvtCollapsiblePanel = function (context, id, buttonImages, x, y, ww, hh, collapseDir) {
  this.Init(context, id, buttonImages, x, y, ww, hh, collapseDir);

};

DvtCollapsiblePanel.COLLAPSE_NORTHEAST = "col_northeast";
DvtCollapsiblePanel.COLLAPSE_SOUTHEAST = "col_southeast";
DvtCollapsiblePanel.ANIMATIONDURATION = 0.25;

DvtCollapsiblePanel.BODYCOLOR = '#EBEFF5';
DvtCollapsiblePanel.TITLEBARCLOSEICON_WIDTH = 10;
DvtCollapsiblePanel.TITLEBARCLOSEICON_HEIGHT = 10;
DvtCollapsiblePanel.BUTTONHEIGHT = 25;

DvtCollapsiblePanel.ALPHA_ROLLOUT_FRAME = 1;
DvtCollapsiblePanel._ALPHA_MOUSEOVER = 1;
DvtCollapsiblePanel._ALPHA_MOUSEOUT = .7;

DvtObj.createSubclass(DvtCollapsiblePanel, DvtContainer, "DvtCollapsiblePanel");

DvtCollapsiblePanel.prototype.Init = function (context, id, buttonImages, x, y, ww, hh, collapseDir) {
  DvtCollapsiblePanel.superclass.Init.call(this, context, id);

  this._lock = false;

  this._dim = new DvtRectangle(0, 0, ww, hh);

  this._buttonImages = buttonImages;
  
  this._isPrintMode = context instanceof DvtXmlContext;
  
  this._collapseDir = collapseDir ? collapseDir : DvtCollapsiblePanel.COLLAPSE_NORTHEAST;
  
  //create the control panel expand button
  var buttonFrame = new DvtContainer(context);
  this._buttonFrame = buttonFrame;
  this.addChild(buttonFrame);
  this._buttonBackground = DvtPanZoomControlPanelLAFUtils.createEmptyViewClosedShape(context, DvtCollapsiblePanel.BUTTONHEIGHT);
  this._buttonContent = DvtPanZoomControlPanelLAFUtils.createEmptyViewClosedFrame(context, DvtCollapsiblePanel.BUTTONHEIGHT);
  this._buttonContent.setAlpha(DvtCollapsiblePanel.ALPHA_ROLLOUT_FRAME);
  this._buttonFrame.addChild(this._buttonContent);
  if (this._isPrintMode)
    this._buttonFrame.setAlpha(0);

  this.renderBackgroundPane();
  
  this._rootPane = new DvtRect(context, 0, 0, this._dim.w, this._dim.h);
  this._rootPane.setTranslateX(this._dim.x);
  this._rootPane.setTranslateY(this._dim.y);
  this.addChild(this._rootPane);

  this._clickIconCallback = DvtObj.createCallback(this, this._click);
  this._tooltipCallback = DvtObj.createCallback(this, this._tooltip);

  this._collapse = false;
  this._setRootPaneState(false);

  this._expandTooltip = 'expand';
  this._collapseTooltip = 'collapse';

  this.addEventListener('mouseover', this.HandleMouseOver, false, this);
  this.addEventListener('mouseout', this.HandleMouseOut, false, this);
}

DvtCollapsiblePanel.prototype.getTitlePane = function () {
  return null;
}

DvtCollapsiblePanel.prototype.getTitlePaneHeight = function () {
  return 0;
}

/*---------------------------------------------------------------------*/
/*   setContentDimensions (dim)                                        */
/*---------------------------------------------------------------------*/
/**  Let developer to change the dimension of the CollapsiblePanel
 *
 *  @param {DvtRectangle} dim   The dimension of the CollapsiblePanel
 *
 */

DvtCollapsiblePanel.prototype.setContentDimensions = function (dim) {
  // Due to restriction in the DvtAnimator, we don't change the x, y of the
  // rootPane, but rather translate it to x, y position
  this._dim = dim;
  this._rootPane.setWidth(dim.w);
  this._rootPane.setHeight(dim.h);
  this._resizeBackgroundPane();
  this._setRootPaneState(false);
  this._buttonFrame.setTranslateY(DvtCollapsiblePanel.BUTTONHEIGHT - this.getTitlePaneHeight());
  this._buttonFrame.setRotation( - 3.1416);
}

DvtCollapsiblePanel.prototype.renderBackgroundPane = function () {
  this._backgroundPane = this._createRoundRectangle(this._dim.x, this._dim.y - this.getTitlePaneHeight(), this._dim.w, this._dim.h + this.getTitlePaneHeight(), true);
  this.addChildAt(this._backgroundPane, 1);
  this._backgroundPane.addChild(this.getTitlePane());
  this._backgroundPane.setAlpha(DvtCollapsiblePanel._ALPHA_MOUSEOUT);
}

DvtCollapsiblePanel.prototype.setBackgroundFill = function (fill) {
  this._backgroundPane.setFill(fill);
}

DvtCollapsiblePanel.prototype.setBackgroundStroke = function (stroke) {
  this._backgroundPane.setStroke(stroke);
}

/**  Let developer to change the dimension of the CollapsiblePanel
 *  @param {DvtRectangle} dim   The dimension of the CollapsiblePanel
 *  @returns {DvtRectangle}  Return the dimension of the CollapsiblePanel
 */
DvtCollapsiblePanel.prototype.getContentDimensions = function () {
  return this._dim;
}

/*
 * Method to be overwritten by subClass.  Return the collapse direction of
 * the collapsible panel.  Valid vaues are: DvtCollapsiblePanel.COLLAPSE_NORTH,
 * DvtCollapsiblePanel.COLLAPSE_NORTHEAST, DvtCollapsiblePanel.COLLAPSE_SOUTHEAST.
 *
 * Default is Collapse_NorthEast
 *
 */
DvtCollapsiblePanel.prototype.GetCollapseDirection = function () {
  return this._collapseDir;
}

/*---------------------------------------------------------------------*/
/*   isCollapse()                                        */
/*---------------------------------------------------------------------*/
/**
 * @returns {boolean}  True if the CollapsiblePanel is collapse.  Default to false
 */

DvtCollapsiblePanel.prototype.isCollapse = function () {
  return this._collapse;
}

/*---------------------------------------------------------------------*/
/*   setCollapse (collapse)                                  */
/*---------------------------------------------------------------------*/
/**  Let developer to change the dimension of the CollapsiblePanel
 *  @param {Boolean} collapse   Set the collapsiblePanel to collapse or not
 */
DvtCollapsiblePanel.prototype.setCollapse = function (collapse) {
  if ((this._collapse && !collapse) || (!this._collapse && collapse)) {
    this._collapse = collapse;
    this._setRootPaneState(true);
  }
}

/*---------------------------------------------------------------------*/
/*   setContent (content)                                              */
/*---------------------------------------------------------------------*/
/**  Let developer to set content pane of the CollapsiblePanel
 *  @param {DvtDisplayable} contnet   The content of the CollapsiblePanel
 */

DvtCollapsiblePanel.prototype.setContent = function (content) {
  this._content = content;
  this._rootPane.addChild(content);
}

/*---------------------------------------------------------------------*/
/*   getContentDimensions ()                                        */
/*---------------------------------------------------------------------*/
/**
 *  @returns {DvtDiaplayable}  Content pane of the CollapsbilePanel
 */

DvtCollapsiblePanel.prototype.getContent = function () {
  return this._content;
}

// Internal:  Unlock the CollapsiblePanel after change
DvtCollapsiblePanel.prototype._restoreClickListener = function () {
  if (this.getContent() != null && this.isCollapse())
    this.getContent().setVisible(false);
  if (this.isCollapse()) {
    this._backgroundPane.setVisible(false);
    this._rootPane.setVisible(false);
  }

  this._lock = false;
}

DvtCollapsiblePanel.prototype._getRefPoint = function (point, isScale) {
  if (!this.isCollapse()) {
    if (isScale)
      return new DvtPoint(1 / point.x, 1 / point.y);
    else 
      return new DvtPoint( - point.x,  - point.y);
  }

  return point;
}

//
// Internal method to update the expand/collapse state of the CollapsiblePanel
//
DvtCollapsiblePanel.prototype._setRootPaneState = function (animate) {
  if (!this.isCollapse() && this.getContent() != null)
    this.getContent().setVisible(true);

  if (!this.isCollapse()) {
    this._backgroundPane.setVisible(true);
    this._rootPane.setVisible(true);
  }

  var dim = this.getContentDimensions();

  var translateY = 0.0;
  if (this.GetCollapseDirection() == DvtCollapsiblePanel.COLLAPSE_SOUTHEAST) {
    translateY = dim.h;
  }

  var parallelPlay = null;

  if (animate) {
    var scalePoint = new DvtPoint(1 / dim.w, 1 / dim.h);
    var scalePoint2 = new DvtPoint(1 / (dim.w), 1 / (dim.h + this.getTitlePaneHeight()));

    var translatePoint = new DvtPoint(dim.w, translateY);
    var translatePoint2 = new DvtPoint(dim.w, translateY + this.getTitlePaneHeight());

    var adjustment = 0;
    if (this.GetCollapseDirection() == DvtCollapsiblePanel.COLLAPSE_SOUTHEAST) {
      adjustment = DvtCollapsiblePanel.BUTTONHEIGHT;
    }
    var translatePoint3 = new DvtPoint(dim.w, translateY - adjustment);

    var fade = null;
    if (this.isCollapse()) {
      fade = new DvtAnimFadeOut(this.getContext(), this._rootPane, DvtCollapsiblePanel.ANIMATIONDURATION);
    }
    else {
      fade = new DvtAnimFadeIn(this.getContext(), this._rootPane, DvtCollapsiblePanel.ANIMATIONDURATION);
    }

    var scaleAnime = new DvtAnimScaleBy(this.getContext(), this._rootPane, this._getRefPoint(scalePoint, true), DvtCollapsiblePanel.ANIMATIONDURATION);
    var scaleAnime2 = new DvtAnimScaleBy(this.getContext(), this._backgroundPane, this._getRefPoint(scalePoint2, true), DvtCollapsiblePanel.ANIMATIONDURATION);
    var moveAnime = new DvtAnimMoveBy(this.getContext(), this._rootPane, this._getRefPoint(translatePoint), DvtCollapsiblePanel.ANIMATIONDURATION);
    var moveAnime3 = new DvtAnimMoveBy(this.getContext(), this._buttonFrame, this._getRefPoint(translatePoint3), DvtCollapsiblePanel.ANIMATIONDURATION);
    var moveAnime2 = new DvtAnimMoveBy(this.getContext(), this._backgroundPane, this._getRefPoint(translatePoint2), DvtCollapsiblePanel.ANIMATIONDURATION);

    parallelPlay = new DvtParallelPlayable(this.getContext(), scaleAnime, scaleAnime2, moveAnime, moveAnime2, moveAnime3, fade);
  }
  else {
    if (this.isCollapse()) {
      this._rootPane.setScaleX(scaleX);
      this._rootPane.setScaleY(scaleY);
      this._rootPane.setTranslateX(translateX);
      this._rootPane.setTranslateY(translateY);
    }
  }

  if (this._clickIcon != null) {
    this._clickIcon.removeEventListener('click', this._clickIconCallback);
    this._clickIcon.removeEventListener('mouseover', this._tooltipCallback);
    this._clickIcon.removeEventListener('mouseout', this._tooltipCallback);
    this._buttonContent.removeChild(this._clickIcon);
  }

  if (!this._isPrintMode) { // do not render collapse/expand tabs in print mode
    if (this.isCollapse())
      this._clickIcon = DvtButtonLAFUtils.createExpandButton(this.getContext(), this._buttonImages, DvtCollapsiblePanel.BUTTONHEIGHT);
    else
      this._clickIcon = DvtButtonLAFUtils.createCollapseButton(this.getContext(), this._buttonImages, DvtCollapsiblePanel.BUTTONHEIGHT);
    
    this._buttonContent.addChild(this._clickIcon);
    this._clickIcon.addEventListener('click', this._clickIconCallback);
    this._clickIcon.addEventListener('mouseover', this._tooltipCallback);
    this._clickIcon.addEventListener('mouseout', this._tooltipCallback);
  }
  
  if (parallelPlay && animate) {
    parallelPlay.setOnEnd(DvtObj.createCallback(this, this._restoreClickListener));
    parallelPlay.play();
  }
  else {
    this._lock = false;
  }
}

DvtCollapsiblePanel.prototype._click = function (evt) {
  if (!this._lock) {
    if (evt.target == this._clickIcon || true) {
      this.getTooltipManager().hideTooltip();
      this._lock = true;
      this.setCollapse(!this.isCollapse());
    }
  }
}

DvtCollapsiblePanel.prototype.getTooltipManager = function () {
  return this.getContext().getTooltipManager();
}

/*
 * Protected: Subclass to overwrite for expand tooltip
 */
DvtCollapsiblePanel.prototype.GetExpandTooltip = function () {
  return this._expandTooltip;
}

/*
 * Protected: Subclass to overwrite for collapse tooltip
 */
DvtCollapsiblePanel.prototype.GetCollapseTooltip = function () {
  return this._collapseTooltip;
}

DvtCollapsiblePanel.prototype._tooltip = function (evt) {
  if (evt.target == this._clickIcon || true) {
    if (evt.type == 'mouseover') {
      var tooltip = this.isCollapse() ? this.GetExpandTooltip() : this.GetCollapseTooltip();
      if (tooltip != null)
        this.getTooltipManager().showTooltip(evt.pageX, evt.pageY, tooltip, this._clickIcon, true);
    }
    else if (evt.type == 'mouseout')
      this.getTooltipManager().hideTooltip();
  }
}

DvtCollapsiblePanel.prototype._createSubId = function (subId) {
  return this.getImpl().getId() + ":" + subId;
}

DvtCollapsiblePanel.prototype._resizeBackgroundPane = function () {
  var height = this._dim.h + this.getTitlePaneHeight();
  var width = this._dim.w;
  var r = 10;
  var cmds;
  if (this._isPrintMode)
    cmds = DvtPathUtils.moveTo(r, 0);
  else
    cmds = DvtPathUtils.moveTo(0, 0);
  cmds += DvtPathUtils.lineTo(width - r, 0);
  cmds += DvtPathUtils.quadTo(width, 0, width, r);
  cmds += DvtPathUtils.lineTo(width, height - r);
  cmds += DvtPathUtils.quadTo(width, height, width - r, height);
  cmds += DvtPathUtils.lineTo(r, height);
  cmds += DvtPathUtils.quadTo(0, height, 0, height - r);
  if (this._isPrintMode) {
    cmds += DvtPathUtils.lineTo(0, r);
    cmds += DvtPathUtils.quadTo(0, 0, r, 0);
  }
  cmds += DvtPathUtils.closePath();
  this._backgroundPane.setCmds(cmds);
  this.setTranslateX(this._dim.x);
  this.setTranslateY(this._dim.y);
}

DvtCollapsiblePanel.prototype._createRoundRectangle = function (x, y, width, height, isFill) {
  var r = 10;
  var cmds;
  if (this._isPrintMode)
    cmds = DvtPathUtils.moveTo(r, 0);
  else
    cmds = DvtPathUtils.moveTo(0, 0);
  //BiDi: when control panel is on right side, bottom left corner is rounded,
  //and bottom right corner is square
  cmds += DvtPathUtils.lineTo(width - r, 0);
  cmds += DvtPathUtils.quadTo(width, 0, width, r);
  cmds += DvtPathUtils.lineTo(width, height - r);
  cmds += DvtPathUtils.quadTo(width, height, width - r, height);
  cmds += DvtPathUtils.lineTo(r, height);
  cmds += DvtPathUtils.quadTo(0, height, 0, height - r);
  if (this._isPrintMode) {
    cmds += DvtPathUtils.lineTo(0, r);
    cmds += DvtPathUtils.quadTo(0, 0, r, 0);
  }
  cmds += DvtPathUtils.closePath();

  var rect = new DvtPath(this.getContext(), cmds);

  this.setTranslateX(x);
  this.setTranslateY(y);

  if (isFill) {
    var rot = 0;
    var colorArray = [DvtButtonLAFUtils.GRADIENT_LIGHT, DvtButtonLAFUtils.GRADIENT_DARK];
    var alphaArray = [1.0, 1.0];
    var ratioArray = [0, 1];

    var xx = Math.PI / 2;
    var yy = Math.PI / 2;

    rect.setFill(new DvtLinearGradientFill(rot, colorArray, alphaArray, ratioArray, [xx, yy, width, height]));
    rect.setStroke(new DvtSolidStroke("#FFFFFF", 1, 2));
  }

  return rect;
}

/**
 * @protected
 */
DvtCollapsiblePanel.prototype.HandleMouseOver = function (event) {
  this._backgroundPane.setAlpha(DvtCollapsiblePanel._ALPHA_MOUSEOVER);
};

/**
 * @protected
 */
DvtCollapsiblePanel.prototype.HandleMouseOut = function (event) {
  this._backgroundPane.setAlpha(DvtCollapsiblePanel._ALPHA_MOUSEOUT);
  this._buttonContent.setAlpha(DvtPanZoomControlPanel.ALPHA_ROLLOUT_FRAME);
};
/**
 * DvtAfShowPopupBehavior
 */
var DvtAfShowPopupBehavior = function() {
  this.Init();
}

/*
 * make DvtAfShowPopupBehavior a subclass of DvtPropMap
 */
DvtObj.createSubclass(DvtAfShowPopupBehavior, DvtPropMap, "DvtAfShowPopupBehavior");

/*
 * Register the DvtAfMarker tag with DvtAfComponentFactory
 */
DvtAfShowPopupBehavior.TAG_NAME = "spb";
DvtAfComponentFactory.registerComponent("dvt", DvtAfShowPopupBehavior.TAG_NAME,
                                        new DvtAfTagHandler(DvtAfShowPopupBehavior));


DvtAfShowPopupBehavior.ATTR_POPUP_ID = "popupId";
DvtAfShowPopupBehavior.ATTR_TRIGGER_TYPE = "triggerType";
DvtAfShowPopupBehavior.ATTR_ALIGN_ID = "alignId";
DvtAfShowPopupBehavior.ATTR_ALIGN = "align";

/**
 * Returns the id of the popup that will be fired by this instance.
 * @return {string} The id of the popup that will be fired.
 */
DvtAfShowPopupBehavior.prototype.getPopupId = function() {
  return this.getProperty(DvtAfShowPopupBehavior.ATTR_POPUP_ID);
}
/**
 * Sets the id of the popup that will be fired by this instance.
 * @param {string} popupId The id of the popup that will be fired.
 */
DvtAfShowPopupBehavior.prototype.setPopupId = function(popupId) {
  this.setProperty(DvtAfShowPopupBehavior.ATTR_POPUP_ID, popupId);
}


/**
 * Returns the interaction type that triggers the popup.
 * @return {string} The the interaction type that triggers the popup.
 */
DvtAfShowPopupBehavior.prototype.getTriggerType = function() {
  return this.getProperty(DvtAfShowPopupBehavior.ATTR_TRIGGER_TYPE);
}
/**
 * Sets the interaction type that triggers the popup.
 * @param {string} triggerType The the interaction type that triggers the popup.
 */
DvtAfShowPopupBehavior.prototype.setTriggerType = function(triggerType) {
  this.setProperty(DvtAfShowPopupBehavior.ATTR_TRIGGER_TYPE, triggerType);
}

/**
 * Returns the id of the object that the popup will be aligned to.
 * @return {string} The id of the object that the popup will be aligned to.
 */
DvtAfShowPopupBehavior.prototype.getAlignId = function() {
  return this.getProperty(DvtAfShowPopupBehavior.ATTR_ALIGN_ID);
}
/**
 * Sets the id of the object that the popup will be aligned to.
 * @param {string} alignId The id of the object that the popup will be aligned to.
 */
DvtAfShowPopupBehavior.prototype.setAlignId = function(alignId) {
  this.setProperty(DvtAfShowPopupBehavior.ATTR_ALIGN_ID, alignId);
}

/**
 * Returns the alignment position for the popup.
 * @return {string} The alignment position for the popup.
 */
DvtAfShowPopupBehavior.prototype.getAlign = function() {
  return this.getProperty(DvtAfShowPopupBehavior.ATTR_ALIGN);
}
/**
 * Sets the alignment position for the popup.
 * @param {string} align The alignment position for the popup.
 */
DvtAfShowPopupBehavior.prototype.setAlign = function(align) {
  this.setProperty(DvtAfShowPopupBehavior.ATTR_ALIGN, align);
}

/*-------------------------------------------------------------------------*/
/*   CreateDisplayObject                                                   */
/*-------------------------------------------------------------------------*/
DvtAfShowPopupBehavior.prototype.createDisplayObj = function(context) {
  // Dont provide align information because the component is rendered in svg
  return new DvtShowPopupBehavior(this.getPopupId(), 
                                  this.getTriggerType());
//                                  this.getAlignId(),
//                                  this.getAlign());
}


// Copyright (c) 2008, 2012, Oracle and/or its affiliates. 
// All rights reserved. 
/*---------------------------------------------------------------------*/
/*   DvtCommonLegend                                                         */
/*   @creater -- Kevin Chow                                            */
/*---------------------------------------------------------------------*/
/**
  *  Creates a legend component
  *  @class DvtCommonLegend
  *  @constructor  
  *  @param {DvtContext} context The context object
  *  @param {x} The leftmost position of the Legend
  *  @param {y} The topmost position of the Legend
  *  @param {ww} The width of the Legend
  *  @param {hh} The height of the Legend
  */
var  DvtCommonLegend = function(context, id, x, y, ww, hh)
{   
  this.Init(context, id, x, y, ww, hh);
};

DvtObj.createSubclass(DvtCommonLegend, DvtObj, "DvtCommonLegend");

DvtCommonLegend.DEFAULT_MARGINX = 5.0;
DvtCommonLegend.DEFAULT_MARGINY = 5.0;

DvtCommonLegend.prototype.Init = function(context, id, x, y, ww, hh)
{
  this._context = context;
  this._id = id;
  this._x = x;
  this._y = y;
  this._ww = ww;
  this._hh = hh;

  this._attrLookUp = new Object();
	this._hideAttrsLookUp = new Object();
	
  this._separators = new Array();
  this._sections = new Array();
  this._sectionGroups = new Array();
  this._sectionGroupSections = new Array();
  this._associations = new Array();

}

DvtCommonLegend.prototype._legendEventHandler = function (evt)
{
}

/*---------------------------------------------------------------------*/
/*   setNoWrap (noWrap)                                                */
/*---------------------------------------------------------------------*/
/**  Determine whether the text inside the legend is wrapped
  *
  *  @param {Boolean} noWrap   True if the text in the legend needs to wrap
  *
  */ 
DvtCommonLegend.prototype.setNoWrap = function (noWrap)
{
  this._noWrap = noWrap;
}

/*---------------------------------------------------------------------*/
/*   isNoWrap ()                                                       */
/*---------------------------------------------------------------------*/
/**  
  *  @returns {Boolean}  Return true if the text in the legend needs to wrap
  */ 
DvtCommonLegend.prototype.isNoWrap = function ()
{
  if (this._noWrap == 'true')
    return this._noWrap;
  else
    return 'false';
}

/*---------------------------------------------------------------------*/
/*   getDisplayForCategory (attrGroupId, listItemId)                   */
/*---------------------------------------------------------------------*/
/**  
  *  @param {String} attrGroupId     The attributeGroupId where the legend Item belongs
  *  @param {String} listItemId      The listItemId where the list Item belongs 
  *  @returns {DvtDisplayable}       Return the displayable of the row for the legend 
  *                                  item from attrGroupId, with id = listItemId
  */ 
DvtCommonLegend.prototype.getDisplayForCategory = function (attrGroupId, listItemId)
{
  var shp = null;

  if (this._attrLookUp[attrGroupId] != null &&
      this._attrLookUp[attrGroupId][listItemId] != null)
  {
    shp = this._attrLookUp[attrGroupId][listItemId].getContentShape();
  }

  return shp;
}

/*---------------------------------------------------------------------*/
/*   getDisplayForAttributeGroupId (attrGroupId)                       */
/*---------------------------------------------------------------------*/
/**  
  *  @param {String} attrGroupId     The attributeGroupId of the group of legend items to retrieve
  *  @returns {Array}                Return an array of the displayable of the legend items in the 
  *                                  atrributeGroup
  *                                  
  */ 
DvtCommonLegend.prototype.getDisplayForAttributeGroup = function (attrGroupId)
{
  var arr = null;

  if (this._attrLookUp[attrGroupId] != null)
  {
    arr = new Array();
    for (x in this._attrLookUp[attrGroupId])
    {
      if (this._attrLookUp[attrGroupId][x] != null &&
          this._attrLookUp[attrGroupId][x].getContentShape() != null)
        arr.push(this._attrLookUp[attrGroupId][x].getContentShape());
    }
  }
  
  return arr;
}

DvtCommonLegend.prototype.addAttrLookup = function (attributeGroupId, itemId, itemContainer)
{
     if (this._attrLookUp[attributeGroupId] == null) this._attrLookUp[attributeGroupId] = new Object();
     this._attrLookUp[attributeGroupId][itemId] = itemContainer;
}

DvtCommonLegend.prototype.addHideAttrsLookup = function (hideAttrs, itemContainer)
{
	if (!this._hideAttrsLookUp[hideAttrs]) 
		this._hideAttrsLookUp[hideAttrs] = itemContainer;
}

DvtCommonLegend.prototype.addAssociation = function (itemContainer, logicalObj)
{
    var stored = new Object();
    stored.item = itemContainer;
    stored.logicalObj = logicalObj;
    this._associations.push(stored);
}

/*---------------------------------------------------------------------*/
/*   setXML (xml)                                                      */
/*---------------------------------------------------------------------*/
/**  Set the xml definition of the content of the legend.  The legend will create 
  *  new content based on the definition.
  *
  *  @param {DvtDomXmlNode} xml   The xml node that contain definition of the legend's content
  *
  */ 
DvtCommonLegend.prototype.setXML = function (xml)
{
  this._parseLegend (xml);
}

DvtCommonLegend.convertMarkerType = function (markerType) 
{
  var mt = markerType;
  
  if (mt == 's' || mt == 'c' || mt == 'h' || mt == 'd' ||
      mt == 'p' || mt == 'tu' || mt == 'td') {
    return mt;
  }
  else 
    return null;
}

DvtCommonLegend.prototype._parseMarker = function (markerNode)
{
  var markerId = markerNode.getAttribute('id');
  var markerUrl = markerNode.getAttribute('url');
  var markerShapeType = markerNode.getAttribute('shapeType');
  var markerColor = markerNode.getAttribute('color');
  var markerOpacity = markerNode.getAttribute('opacity');
  return new DvtLegendListItemMarker(markerId, markerUrl, markerShapeType, markerColor, markerOpacity);
}

DvtCommonLegend.prototype._parseText = function (textNode)
{
  var textId = textNode.getAttribute('id');
  var textLabel = textNode.getAttribute('value');
  return textLabel;
}

DvtCommonLegend.prototype._parseItem = function (itemNode, sectionObj)
{
     var itemId = itemNode.getAttribute('itemId');
     var attributeGroupId = itemNode.getAttribute('attributeGroupId');

     var prop = new Object();
     prop ['itemId'] = itemId;
     prop ['attributeGroupId'] = attributeGroupId;
     prop['hideAttrs'] = itemNode.getAttribute('hideAttrs');
     
     var legendListItem = new DvtLegendListItem(this, prop);
     sectionObj.addItem(legendListItem);

     var itemChildren = itemNode.getChildNodes();

     for (var i=0; i < itemChildren.length; i++)
     {         
         if (itemChildren[i].getName() == 'marker')
         {
             var marker = this._parseMarker(itemChildren[i]);
             legendListItem.setMarker(marker);
         } else if (itemChildren[i].getName() == 'text')
         {
             var text = this._parseText(itemChildren[i]);                   
             legendListItem.setText(text);
         }
     }
}

DvtCommonLegend.prototype._parseLegendSection = function (legendSectionNode)
{
    var sectionId = legendSectionNode.getAttribute('id');
    var sectionLabel = legendSectionNode.getAttribute('label');

    var sectionObj = new DvtLegendSection(this, sectionId, sectionLabel);

    var childNodes = legendSectionNode.getChildNodes();
    if (childNodes) {
      for (var i = 0; i < childNodes.length; i++) {
        var childNode = childNodes[i];
        if (childNode.getName() == 'item') {
          this._parseItem(childNode, sectionObj);          
        } else if (childNode.getName() == 'text') {
          this._parseItemText(childNode, sectionObj);
        }
      }
    }
    return sectionObj;
}

DvtCommonLegend.prototype._parseItemText = function (itemNode, sectionObj)
{
     var prop = new Object();
     var legendListItem = new DvtLegendListTextItem(this, prop);
     sectionObj.addItem(legendListItem);

     var text = this._parseText(itemNode);                   
     legendListItem.setText(text);
}

DvtCommonLegend.prototype._parseLegendSectionGroup = function (legendSectionGroupNode)
{
  if (legendSectionGroupNode.getName() == 'sectionGroup') {
    var sectionGroupId = legendSectionGroupNode.getAttribute('id');
    var sectionGroupLabel = legendSectionGroupNode.getAttribute('label');
    var sectionGroupDisclosed = legendSectionGroupNode.getAttribute('disclosed');

    var showDetail = new DvtAfShowDetail();
    showDetail.setId('sd');
    showDetail.setDisclosedText(sectionGroupLabel);
    showDetail.setUnDisclosedText(sectionGroupLabel);
    showDetail.setExpandTooltip(legendSectionGroupNode.getAttribute('expandTooltip'));
    showDetail.setCollapseTooltip(legendSectionGroupNode.getAttribute('collapseTooltip'));
    showDetail.setDisclosed(sectionGroupDisclosed);
    this.addSectionGroup(showDetail);

    var childNodes = legendSectionGroupNode.getChildNodes();
    if (childNodes) {
      var sections = [];
      for (var i = 0; i < childNodes.length; i++) {
        var childNode = childNodes[i];
        if (childNode.getName() == 'section') {
         sections.push(this._parseLegendSection(childNode));     
        }
      }
      this.addSectionGroupSection(sections);
    }
  }
}

DvtCommonLegend.prototype._parseLegend = function (legendNode)
{
  if (legendNode.getName() == 'legend')
  {
    this._title = legendNode.getAttribute('label');
    this._paletteId = legendNode.getAttribute('id');
    var noWrap = legendNode.getAttribute('noWrap');
    this.setNoWrap(noWrap);
 
    var legendDisclosed = legendNode.getAttribute('disclosed');
    // Don't change the UI yet.  Delay until getContentDimension is set
    if (legendDisclosed == 'false')    
        this._collapse = true;    

    this._expandTooltip = legendNode.getAttribute('expandTooltip');
    this._collapseTooltip = legendNode.getAttribute('collapseTooltip');
   
    var foundSeparator = false;
    var childNodes = legendNode.getChildNodes();
    if (childNodes) {
      for (var i = 0; i < childNodes.length; i++) {
        var childNode = childNodes[i];
        
        if (!foundSeparator && childNode.getName() == 'separatorDef') {
          foundSeparator = true;
          this._separator = true;
        }
        else if (childNode.getName() == 'sectionGroup') {
          this._parseLegendSectionGroup(childNode);          
        } 
        else if (childNode.getName() == 'section') {
          this.addSection(this._parseLegendSection(childNode));          
        }
      }
    }
  }
}

DvtCommonLegend.prototype.addSection = function(section)
{
    this._sections.push(section);
}

DvtCommonLegend.prototype.addSectionGroup = function(group)
{
    this._sectionGroups.push(group);
}

DvtCommonLegend.prototype.addSectionGroupSection = function(section)
{
    this._sectionGroupSections.push(section);
}

DvtCommonLegend.prototype._renderSeparator = function (container)
{
    if (this._separator) {
        var sepContainer = new DvtAfPanelGroupLayout();
        sepContainer.setLayout(DvtAfPanelGroupLayout.LAYOUT_HORIZONTAL);
        sepContainer.setInlineStyle('width:1px;height:9px');
        sepContainer.setVAlign(DvtAfPanelGroupLayout.VALIGN_MIDDLE);
        var separator = new DvtAfSeparator();
        sepContainer.addChild(separator);           
        container.addChild(sepContainer);
        this._separators.push(separator);
    }
}

DvtCommonLegend.prototype.getDisplayObj = function ()
{
  return this._container.getDisplayObj();
}

DvtCommonLegend.prototype.render = function (parent)
{
  this._legendContainer = parent;
  this._associations = new Array();
  this._separators = new Array();

  var container = new DvtAfPanelGroupLayout();
  container.setLayout(DvtAfPanelGroupLayout.LAYOUT_VERTICAL);
  this._container = container;
  
  if (this._title) {
      var titleBar = new DvtAfPanelGroupLayout();
      titleBar.setLayout(DvtAfPanelGroupLayout.LAYOUT_HORIZONTAL);
      container.addChild(titleBar);
      var legendTitle = new DvtAfOutputText();
      legendTitle.setNoWrap(this.isNoWrap());
      legendTitle.setValue(this._title);    
      titleBar.addChild(legendTitle);
  }
  
  var section;
  if (this._sectionGroups.length > 0) {
    var sectionGroup;
    for (var i=0; i<this._sectionGroups.length; i++) {
      sectionGroup = this._sectionGroups[i];
      container.addChild(sectionGroup);
      sectionGroup.setParentContainer(parent);
      // Render all sections in a section group
       for (var j=0; j<this._sectionGroupSections[i].length; j++) {
        section = this._sectionGroupSections[i][j];
        section.render(sectionGroup);
       }
    }
  } else {
    for (var i=0; i<this._sections.length;i++) {
      if ( ((i == 0) && this._title) || i > 0 ) {
          this._renderSeparator(container);
      }
      section = this._sections[i];
      section.render(container);
    }
  }
  

  this._eventHandler = new DvtLegendEventManager(this._context, this._legendEventHandler, this);

  this.LayoutAndAdd(parent);
  
  this._eventHandler.addListeners(container.getDisplayObj());

  for (var i=0;i<this._associations.length;i++) {
    var association = this._associations[i];
    this._eventHandler.associate(association.item.getDisplayObj(), association.logicalObj);
  }
}

DvtCommonLegend.prototype.LayoutAndAdd = function (parent)
{
  var afContext = new DvtAfContext(this._context, this._eventHandler);
  var dimensions = new DvtRectangle(this._x, this._y, this._ww, this._hh);
  var strWidth = dimensions.w.toString() + 'px';
  var strHeight = dimensions.h.toString() + 'px';
  afContext.setAvailableWidth(strWidth);
  afContext.setAvailableHeight(strHeight);
  afContext.setFontSize('32px');  

  this._content = DvtAfComponentFactory.doLayout (afContext, this._container, parent);  
  this._content.setTranslateX(DvtCommonLegend.DEFAULT_MARGINX);
  this._content.setTranslateY(DvtCommonLegend.DEFAULT_MARGINY);
  
  var dim = this._content.getDimensions();
  dim.w += DvtCommonLegend.DEFAULT_MARGINX * 2.0;
  dim.h += DvtCommonLegend.DEFAULT_MARGINY * 2.0;

  for (var i=0;i<this._separators.length;i++) {
    var separator = this._separators[i];      
    separator._line1.setX2(dim.w - 10);
    separator._line2.setX2(dim.w - 10);
  }
}

var  DvtBaseSection = function(mainComp, id, label)
{   
    this.Init(mainComp, id, label);
};

DvtObj.createSubclass(DvtBaseSection, DvtObj, "DvtBaseSection");

DvtBaseSection.prototype.Init = function(mainComp, id, label) {
    this._id = id;
    this._label = label;
    this._items = new Array();
    this._mainComp = mainComp;
}
DvtBaseSection.prototype.getId = function() {
    return this._id;
}
DvtBaseSection.prototype.getLabel = function() {
    return this._label;
}
DvtBaseSection.prototype.addItem = function(item) {
    return this._items.push(item);
}
DvtBaseSection.prototype.getItems = function() {
    return this._items;
}

DvtBaseSection.prototype.CreateSectionRoot = function() {
    var container = new DvtAfPanelGroupLayout();
    container.setLayout(DvtAfPanelGroupLayout.LAYOUT_VERTICAL);
    return container;
}

DvtBaseSection.prototype.AddSeparator = function(container) {
}

DvtBaseSection.prototype.render = function(parent) {
    var sectionLabel = this._label;
    var sectionId = this._id;

    if (sectionLabel != null)
    {
        var sectionLabelText = new DvtAfOutputText();
        sectionLabelText.setId(sectionId);
        sectionLabelText.setValue(sectionLabel);
        sectionLabelText.setNoWrap(this._mainComp.isNoWrap());
        sectionLabelText.setInlineStyle('padding-top:2px;padding-bottom:2px');
        parent.addChild(sectionLabelText);
    }

    var container = this.CreateSectionRoot();
    //container.setInlineStyle("width:100px;");
    parent.addChild(container);
    
    for (var i=0;i<this._items.length;i++) {
        var item = this._items[i];
        if (i > 0) {
            this.AddSeparator(container);
        }
        item.render(container);
    }
}

var  DvtLegendSection = function(legend, id, label)
{   
    this.Init(legend, id, label);
};

DvtObj.createSubclass(DvtLegendSection, DvtBaseSection, "DvtLegendSection");

DvtLegendSection.prototype.Init = function(legend, id, label) {
    DvtLegendSection.superclass.Init.call(this, legend, id, label);
    this._legend = legend;
}

var  DvtLegendListTextItem = function(legend, prop)
{   
    this.Init(legend, prop);
};

DvtObj.createSubclass(DvtLegendListTextItem, DvtObj, "DvtLegendListTextItem");

DvtLegendListTextItem.prototype.Init = function(legend, prop) {
    this._legend = legend;
    this._prop = prop;
}

DvtLegendListTextItem.prototype.setText = function(text) {
    this._text = text;
}

DvtLegendListTextItem.prototype.render = function(parent) {
    var itemContainer = new DvtAfPanelGroupLayout();
    itemContainer.setLayout(DvtAfPanelGroupLayout.LAYOUT_HORIZONTAL);
    parent.addChild(itemContainer);
    itemContainer.setInlineStyle("background-color:rgba(0,0,0,0.001);");
    if (this._text) {
        var text = new DvtAfOutputText();
        text.setValue(this._text);
        text.setNoWrap(this._legend.isNoWrap());
        itemContainer.addChild(text);
    }
}

var  DvtLegendListItem = function(legend, prop)
{   
    this.Init(legend, prop);
};

DvtObj.createSubclass(DvtLegendListItem, DvtObj, "DvtLegendListItem");

DvtLegendListItem.prototype.Init = function(legend, prop) {
    this._legend = legend;
    this._prop = prop;
}

DvtLegendListItem.prototype.setText = function(text) {
    return this._text = text;
}

DvtLegendListItem.prototype.setMarker = function(marker) {
    return this._marker = marker;
}

DvtLegendListItem.prototype.render = function(parent) {
     var itemContainer = new DvtAfPanelGroupLayout();

     itemContainer.setLayout(DvtAfPanelGroupLayout.LAYOUT_HORIZONTAL);
     parent.addChild(itemContainer);

     itemContainer.setInlineStyle("background-color:rgba(0,0,0,0.001);");

     this._legend.addAttrLookup(this._prop['attributeGroupId'], this._prop['itemId'], itemContainer);
     this._legend.addAssociation(itemContainer, this);
		 this._legend.addHideAttrsLookup(this._prop['hideAttrs'], itemContainer);
    
    var spacer = new DvtAfSpacer();
    spacer.setWidth('5px');
    spacer.setHeight('5px');
    itemContainer.addChild(spacer);

    if (this._marker) {
        this._marker.render(itemContainer);
    }

    if (this._text) {
        var spacer = new DvtAfSpacer();
        spacer.setWidth('5px');
        spacer.setHeight('5px');
        itemContainer.addChild(spacer);
        var text = new DvtAfOutputText();
        text.setValue(this._text);
        text.setNoWrap(this._legend.isNoWrap());
        itemContainer.addChild(text);
    }


}

var  DvtLegendListItemMarker = function(markerId, markerUrl, markerShapeType, markerColor, markerOpacity)
{   
    this.Init(markerId, markerUrl, markerShapeType, markerColor, markerOpacity);
};

DvtObj.createSubclass(DvtLegendListItemMarker, DvtObj, "DvtLegendListItemMarker");

DvtLegendListItemMarker.prototype.Init = function(markerId, markerUrl, markerShapeType, markerColor, markerOpacity) {
    this._markerId = markerId;
    this._markerUrl = markerUrl;
    this._markerShapeType = markerShapeType;
    this._markerColor = markerColor;
    this._markerOpacity = markerOpacity;
}

DvtLegendListItemMarker.prototype.getVisualMarker = function() {
    return this._visualMarker;
}

DvtLegendListItemMarker.prototype.render = function(parent) {
  if (this._markerUrl != null)
  {
    var image = new DvtImage();
    image.setSrc(this._markerUrl);
    return image;
  } else
  {

    var marker1 = new DvtAfMarker();
        
    // To Do: instead of 11.0, use real text height
    marker1.setWidth(11.0);
    marker1.setHeight(11.0);
    marker1.setFillColor(this._markerColor);    
    var convertedMarkerType = DvtCommonLegend.convertMarkerType(this._markerShapeType);
    if (! convertedMarkerType) {
      // Unrecognized shape, assume this is shapePath
      marker1.setShapePath(this._markerShapeType);
    }
    else {
      marker1.setShape(convertedMarkerType);
    }
    parent.addChild(marker1); 
    this._visualMarker = marker1;
  }
}

var DvtLegendEventManager = function(context, callback, callbackObj) {
  this.Init(context, callback, callbackObj);
  this._legend = callbackObj;
};

DvtObj.createSubclass(DvtLegendEventManager, DvtEventManager, "DvtLegendEventManager");

/**
 * override
 */
DvtLegendEventManager.prototype.OnClick = function(event) {
  DvtLegendEventManager.superclass.OnClick.call(this, event);
  var obj = this.GetLogicalObject(event.target);
  if (obj instanceof DvtLegendListItem) {
      this._clickListener(event, obj);
  }
}

DvtLegendEventManager.prototype.OnMouseOver = function(event) {
  DvtLegendEventManager.superclass.OnMouseOver.call(this, event);
  var obj = this.GetLogicalObject(event.target);
  if (obj instanceof DvtLegendListItem) {
      this._rollOverListener(event, obj);
  }
}

DvtLegendEventManager.prototype.OnMouseOut = function(event) {
  DvtLegendEventManager.superclass.OnMouseOut.call(this, event);
  var obj = this.GetLogicalObject(event.target);
  if (obj instanceof DvtLegendListItem) {
      this._rollOverListener(event, obj);
  }
}

DvtLegendEventManager.prototype._clickListener = function (evt, listItem) 
{
      var listItemId = (listItem._prop != null) ? listItem._prop['itemId'] : null;      
      var attributeGroupId = (listItem._prop != null) ? listItem._prop['attributeGroupId'] : null;      
      
      var showHide = DvtListItemEvent.HIDE;
      
      var marker = listItem._marker.getVisualMarker().getMarkerDisplayObj();
          if (marker) {
          if (!marker._originalAlpha)
          {
              var fill = marker.getFill().clone();
              marker._originalAlpha = fill.getAlpha();
              fill.setAlpha(0.0);
              marker.setFill(fill);
    
              var c = fill.getColor();
              var stroke = new DvtSolidStroke(c, 1.0, 1.0);
              marker._originalStroke = marker.getStroke();
              marker.setStroke(stroke);
          } else
          {
              showHide = DvtListItemEvent.SHOW;
    
              var fill = marker.getFill().clone();
              fill.setAlpha(marker._originalAlpha);
              marker.setFill(fill);
              marker.setStroke(marker._originalStroke);
              marker._originalAlpha = null;
              marker._originalStroke = null;
          }
      }

      var listItemEvent = new DvtListItemEvent (listItemId, attributeGroupId, showHide, evt);
      this._legend.getDisplayObj().FireListener(listItemEvent);      
}

DvtLegendEventManager.prototype._rollOverListener = function (evt, listItem) 
{
      if (listItem._prop == null)
          return;

      var listItemId = listItem._prop['itemId'];      
      var attributeGroupId = listItem._prop['attributeGroupId'];
      var hideAttr = listItem._prop['hideAttrs'];
      var rollOverEvent = new DvtLegendItemRollOverEvent (listItemId, attributeGroupId, hideAttr, evt.type, evt);
      this._legend.getDisplayObj().FireListener(rollOverEvent);      
}
// Copyright (c) 2008, 2012, Oracle and/or its affiliates. 
// All rights reserved. 
/*---------------------------------------------------------------------*/
/*   DvtOverviewWindow                                                 */
/*   @creater -- Kevin Chow                                            */
/*---------------------------------------------------------------------*/
/**
  *  Creates an Overview window component
  *  @class DvtOverviewWindow
  *  @extends DvtCollapsiblePanel
  *  @constructor  
  *  @param {DvtContext} context The context object
  *  @param {x} The leftmost position of the Legend
  *  @param {y} The topmost position of the Legend
  *  @param {ww} The width of the Legend
  *  @param {hh} The height of the Legend
  */
var  DvtOverviewWindow = function(context, id, x, y, ww, hh)
{   
   this.Init(context, id, x, y, ww, hh);
};

DvtObj.createSubclass(DvtOverviewWindow, DvtObj, "DvtOverviewWindow");

DvtOverviewWindow.INIT_SCALE_PERCENT = 0.75;

DvtOverviewWindow.prototype.Init = function(context, id, x, y, ww, hh, page)
{
  this._context = context;
  this._id = id;
  this._x = x;
  this._y = y;
  this._ww = ww;
  this._hh = hh;
  this._page = page;

  //flag for whether events should be fired continuously as the viewport
  //is dragged, or only at the end when the moue is released
  this._bContinuousEvents = false;
  //flag for whether the viewport is specified and sends events in the 
  //coordinate system of the content, or of the overview window
  this._bViewportInContentCoords = false;

}

DvtOverviewWindow.prototype.render = function(parent)
{    
  var rect = new DvtRect (this._context, 0.0, 0.0, 0.0, 0.0, this._id+":"+'viewport');
  
  this._rootContent = parent;
  
  //Overview area color
  rect.setStroke(new DvtSolidStroke("#05228F", 1.0, 1.0));
  rect.setFill(new DvtSolidFill("#05228F", 0.2));

  this._viewportMouseDownListener = DvtObj.createCallback(this, this._mouseDown);
  this._viewportMouseMoveListener = DvtObj.createCallback(this, this._mouseMove);
  this._viewportMouseUpListener = DvtObj.createCallback(this, this._mouseUp);

  rect.addEventListener('mousedown', this._viewportMouseDownListener);
  rect.addEventListener('mousemove', this._viewportMouseMoveListener);
  rect.addEventListener('mouseup', this._viewportMouseUpListener);  

  var clipPath = new DvtClipPath('c1');
  clipPath.addRect(0.0, 0.0, this._ww, this._hh);
  
  this._clipContainer = new DvtContainer(this._context, this._id+":"+'clipContainer');
  this._clipContainer.setClipPath(clipPath);
  parent.addChild(this._clipContainer);
  
  this._viewport = rect; 
  this._clipContainer.addChild(this._viewport);
  
  this._md = false;
  this._panEnable = true;
  
}

DvtOverviewWindow.prototype.getTitlePane = function ()
{
  return null;//new DvtText(this.getContext(), "Overview", 30.0, 2.0);
}

DvtOverviewWindow.prototype.getTitlePaneHeight = function ()
{
  return 0;//20.0;
}

/*---------------------------------------------------------------------*/
/*   static createOverviewWindow                                       */
/*---------------------------------------------------------------------*/
/**  A factory method for contructing an OverviewWindow
  *
  *  @param {DvtContext} context         The context object
  *  @param (DvtAbstrcatComponent) view  The viewObject that this overviewWindow attached to 
  *  @param {DvtXmlNode} ovNode          The XML node contains the definition of the overviewWindow
  *  @return  A new overviewWindow
  *
  */ 
DvtOverviewWindow.createOverviewWindow = function (context, view, ovNode) 
{
  if (ovNode != null && ovNode.getName() == 'overviewWindow')         
  {
      var ovWidth = 200;
      var ovHeight = 150;
      var ovX = view._width - ovWidth;
      var ovY = view._height - ovHeight;
      var ovWindow = new DvtOverviewWindow(context, 'overviewWindow', ovX, ovY, ovWidth, ovHeight);
      
      if (view.setOverviewWindow)
        view.setOverviewWindow(ovWindow);
        
      return ovWindow;
  }

  return null;
}

DvtOverviewWindow.prototype.loadXmlNode = function (ovNode) 
{
  //TODO:
};

DvtOverviewWindow.prototype.getContentAreaWidth = function ()
{
  return this.getContentDimensions().w;
}

DvtOverviewWindow.prototype.getContentAreaHeight = function ()
{
  return this.getContentDimensions().h;
}

DvtOverviewWindow.prototype.getContentDimensions = function () 
{
    return new DvtRectangle(this._x, this._y, this._ww, this._hh);
}

/*---------------------------------------------------------------------*/
/*   setViewportDimensions                                             */
/*---------------------------------------------------------------------*/
/**  Set the dimension of the viewport within the overviewWindow.  The coordinate should be
  *  relative to the content pane
  *
  *  @param {DvtRectangle} dim           The dimension of the viewport
  *
  */ 
DvtOverviewWindow.prototype.setViewportDimensions = function (dim)
{
  if (!this._md)
  {
    if (this._bViewportInContentCoords)
    {
      var topLeft = this.TransformFromContentToViewportCoords(dim.x, dim.y);
      var bottomRight = this.TransformFromContentToViewportCoords(dim.x + dim.w, dim.y + dim.h);
      this._viewport.setX(topLeft.x);
      this._viewport.setY(topLeft.y);
      this._viewport.setWidth(bottomRight.x - topLeft.x);
      this._viewport.setHeight(bottomRight.y - topLeft.y);
    }
    else
    {
      this._viewport.setX(dim.x);
      this._viewport.setY(dim.y);
      this._viewport.setWidth(dim.w);
      this._viewport.setHeight(dim.h);
    }
  }
}

/*---------------------------------------------------------------------*/
/*   getViewportDimensions ()                                          */
/*---------------------------------------------------------------------*/
/**  
  *  @returns {DvtRectangle}  Return the dimension of the viewport
  */ 
DvtOverviewWindow.prototype.getViewportDimensions = function ()
{
  if (this._bViewportInContentCoords)
  {
    var topLeft = this.TransformFromViewportToContentCoords(
                          this._viewport.getX(), this._viewport.getY());
    var bottomRight = this.TransformFromViewportToContentCoords(
                          this._viewport.getX() + this._viewport.getWidth(), 
                          this._viewport.getY() + this._viewport.getHeight());
    return new DvtRectangle(topLeft.x, topLeft.y, 
                            bottomRight.x - topLeft.x,
                            bottomRight.y - topLeft.y);
  }
  else
  {
    return new DvtRectangle (this._viewport.getX(),
                             this._viewport.getY(),
                             this._viewport.getWidth(),
                             this._viewport.getHeight()); 
  }
}

/*---------------------------------------------------------------------*/
/*   setPanEnabled                                                     */
/*---------------------------------------------------------------------*/
/**  Determine whether the viewport is pannable or not
  *
  *  @param {Boolean} pan         True if you can pan the viewport
  *
  */ 

DvtOverviewWindow.prototype.setPanEnabled = function (pan)
{
  this._panEnable = pan;
}

/*---------------------------------------------------------------------*/
/*   isPanEnabled                                                      */
/*---------------------------------------------------------------------*/
/**  
  *  @returns {Boolean}  Return true if the viewport is pannable
  */ 
DvtOverviewWindow.prototype.isPanEnabled = function ()
{
  return this._panEnable;
}

DvtOverviewWindow.prototype.setContent = function (content)
{  
  var oldDims = null;
  if (this._bViewportInContentCoords)
  {
    oldDims = this.getViewportDimensions();
  }

  this._content = content;

  //move the content from the rootPane to the clipContainer
  this._clipContainer.addChild(content);
  
  //if viewport is specified in content coords, then re-set the 
  //dimensions now that we have content
  if (oldDims)
  {
    this.setViewportDimensions(oldDims);
  }
};

DvtOverviewWindow.prototype.setContinuousEvents = function(bContinuous)
{
  this._bContinuousEvents = bContinuous;
};

DvtOverviewWindow.prototype.isContinuousEvents = function()
{
  return this._bContinuousEvents;
};

DvtOverviewWindow.prototype.setViewportInContentCoords = function(bContentCoords)
{
  this._bViewportInContentCoords = bContentCoords;
};

DvtOverviewWindow.prototype.isViewportInContentCoords = function()
{
  return this._bViewportInContentCoords;
};

DvtOverviewWindow.prototype._mouseDown = function (evt)
{
  if (!this._md && this._panEnable)
  {
      this._md = true;
      this._oldDim = this.getViewportDimensions();
      this._clickX = evt.offsetX;
      this._clickY = evt.offsetY;
      this._originalX = evt.target.getX();
      this._originalY = evt.target.getY();
  }
}

DvtOverviewWindow.prototype._mouseMove = function (evt)
{
  if (this._md && this._panEnable)  
  {
      var newX = evt.offsetX - this._clickX + this._originalX;
      var newY = evt.offsetY - this._clickY + this._originalY;
            
      evt.target.setX(newX);
      evt.target.setY(newY);

      if (this._bContinuousEvents)
      {
        var viewportDims = this.getViewportDimensions();
        var viewportEvent = new ViewportChangeEvent (this._oldDim, viewportDims, evt);
        this.getDisplayObj().FireListener(viewportEvent);
        
        //update stored values
        this._oldDim = viewportDims;
        this._clickX = evt.offsetX;
        this._clickY = evt.offsetY;
        this._originalX = evt.target.getX();
        this._originalY = evt.target.getY();
      }
  }
}

DvtOverviewWindow.prototype._mouseUp = function (evt)
{  
  if (this._md && this._panEnable)
  {
      this._md = false;

      if (!this._bContinuousEvents)
      {
        var viewportEvent = new ViewportChangeEvent (this._oldDim, this.getViewportDimensions(), evt);
        this.getDisplayObj().FireListener(viewportEvent);          
      }
      
      this._oldDim = null;
  }
}



/**
 * @protected
 */
DvtOverviewWindow.prototype.TransformFromViewportToContentCoords = function(vx, vy)
{
  var tx = 0;
  var ty = 0;
  var sx = 1;
  var sy = 1;
  if (this._content)
  {
    tx = this._content.getTranslateX();
    ty = this._content.getTranslateY();
    sx = this._content.getScaleX();
    sy = this._content.getScaleY();
  }
  
  var cx = (vx - tx) / sx;
  var cy = (vy - ty) / sy;
  return new DvtPoint(cx, cy);
};

/**
 * @protected
 */
DvtOverviewWindow.prototype.TransformFromContentToViewportCoords = function(cx, cy)
{
  var tx = 0;
  var ty = 0;
  var sx = 1;
  var sy = 1;
  if (this._content)
  {
    tx = this._content.getTranslateX();
    ty = this._content.getTranslateY();
    sx = this._content.getScaleX();
    sy = this._content.getScaleY();
  }
  
  var vx = (cx * sx) + tx;
  var vy = (cy * sy) + ty;
  return new DvtPoint(vx, vy);
};

DvtOverviewWindow.prototype.getDisplayObj = function ()
{
  return this._rootContent;
}

// Panel used for marquee operations
var   DvtMarqueePanel = function(context)
{
    this.Init(context);
}  

DvtObj.createSubclass(DvtMarqueePanel, DvtContainer, "DvtMarqueePanel");

DvtMarqueePanel.DEFAULT_BACKGROUND_COLOR = "#ffffff";

DvtMarqueePanel.prototype.Init = function(context) {
   if (! this.getImpl()) {
     this.setImpl(context.getImplFactory().newContainer("marqueepanel")) ;
   }
   this._backgroundFill = new DvtSolidFill(DvtMarqueePanel.DEFAULT_BACKGROUND_COLOR, 0.6);
   this._panelWidth = 100;
   this._panelHeight = 100;
   var rect = new DvtRect(context, 0,0,this._panelWidth,this._panelHeight);
   rect.setFill(this._backgroundFill);
   this._rect = rect;
   this.addChild(rect);

   this.setMouseEnabled(false);
   rect.setMouseEnabled(false);
   
   this.setBandAttributes(1, "orange");
}

DvtMarqueePanel.prototype.Render = function() {
    this._rect.setWidth(this._panelWidth);
    this._rect.setHeight(this._panelHeight);
}

DvtMarqueePanel.prototype.setPanelSize = function(width, height) {
    this._panelWidth = width;
    this._panelHeight = height;
}

DvtMarqueePanel.prototype.setBandAttributes = function(bandLineWidth, bandColor) {
   this.m_iBandWidth = bandLineWidth ;
   this.m_iBandColor = bandColor ;
    var stroke = new DvtSolidStroke(this.m_iBandColor, 1, this.m_iBandWidth);
    this._rect.setStroke(stroke);
}
/**
 * Base attribute groups handler.
 * @class
 * @constructor
 */
var DvtAttrGroups = function() {}

DvtObj.createSubclass(DvtAttrGroups, DvtObj, "DvtAttrGroups");

/**
 * Returns the mapping for the specified group or value.
 * @param {object} value The group or value whose mapping will be retrieved.
 * @return {object} The corresponding value along the ramp.
 */
DvtAttrGroups.prototype.get = function(value) {
  // subclasses must override
};
/**
 * Discrete attribute groups handler.
 * @class
 * @constructor
 * @extends {DvtAttrGroups}
 */
var DvtDiscreteAttrGroups = function() {
  this._results = new Array();
}

DvtObj.createSubclass(DvtDiscreteAttrGroups, DvtAttrGroups, "DvtDiscreteAttrGroups");

/**
 * Adds a mapping to this object.
 * @param {string} group The id for the group.
 * @param {string} groupLabel The label for the group.
 * @param {object} params
 */
DvtDiscreteAttrGroups.prototype.add = function(group, groupLabel, params) {
  this._results.push({group: group, groupLabel: groupLabel, params: params});
}

/**
 * Returns the mapping for the specified group.
 * @param {object} group The group whose mapping will be retrieved.
 * @return {object} The parameters object
 */
DvtDiscreteAttrGroups.prototype.get = function(group) {
  if(value === null)
    return null;

  // Loop through the results to find the mapping
  for(var i=0; i<this._results.length; i++) {
    if(this._results[i].group == group)
      return this._results[i].params;
  }
  
  // Otherwise not found
  return null;
};

/**
 * Returns the array of mapping objects.  Each object contains the mapped value, the
 * group id, and the group label.
 * @return {array} An array of objects with value, group, and groupLabel fields.
 */
DvtDiscreteAttrGroups.prototype.getMappingsArray = function() {
  return this._results.slice(0);
}
/**
 * Continuous attribute groups handler.
 * @param {number} minValue The minimum bounds for use in mapping values.
 * @param {number} maxValue The maximum bounds for use in mapping values.
 * @param {string} minLabel The label for the minimum bounds.
 * @param {string} maxLabel The label for the maximum bounds.
 * @param {array} ramp The ramp for use in mapping values.
 * @class
 * @constructor
 * @extends {DvtAttrGroups}
 */
var DvtContinuousAttrGroups = function(minValue, maxValue, minLabel, maxLabel, ramp) {
  this._minValue = minValue;
  this._maxValue = maxValue;
  this._minLabel = minLabel ? minLabel : this._minValue;
  this._maxLabel = maxLabel ? maxLabel : this._maxValue;
  this._ramp = ramp;
  
  // Cache the range for performance
  this._range = this._maxValue - this._minValue;
}

DvtObj.createSubclass(DvtContinuousAttrGroups, DvtAttrGroups, "DvtContinuousAttrGroups");

/**
 * Returns the mapping for the specified value.
 * @param {object} value The value whose mapping will be retrieved.
 * @return {object} The corresponding value along the ramp.
 */
DvtContinuousAttrGroups.prototype.get = function(value) {
  if(isNaN(value) || value === null)
    return null;

  // Calculate the ratio along the ramp
  var ratio = (value - this._minValue)/this._range;
  ratio = Math.max(Math.min(ratio, 1), 0);
  
  // Find the surrounding object mappings
  var index = ratio * (this._ramp.length-1);
  if(index === Math.round(index))
    return this._ramp[index];
  else {
    // Index falls between two values
    var a = this._ramp[Math.floor(index)];
    var b = this._ramp[Math.ceil(index)];
    return this._calcValue(a, b, index - Math.floor(index));
  }
};

/**
 * Returns the ramp used for mapping values.
 * @return {array}
 */
DvtContinuousAttrGroups.prototype.getRamp = function() {
  return this._ramp.slice(0);
}

/**
 * Returns the label for the minimum bounds.
 * @return {string}
 */
DvtContinuousAttrGroups.prototype.getMinLabel = function() {
  return this._minLabel;
}

/**
 * Returns the label for the maximum bounds.
 * @return {string}
 */
DvtContinuousAttrGroups.prototype.getMaxLabel = function() {
  return this._maxLabel;
}

/**
 * Returns the value between the specified parameters at the specified percent.
 * @param {object} a
 * @param {object} b
 * @param {number} percent The percent between a and b.
 * @return {object} The value at the specified percent between a and b.
 */
DvtContinuousAttrGroups.prototype._calcValue = function(a, b, percent) {
  // Note: Only color is supported by continuous attribute groups in this release.
  return DvtColorUtils.interpolateColor(a, b, percent);
}
// Copyright (c) 2008, 2012, Oracle and/or its affiliates. 
// All rights reserved. 

/**
 * Breadcrumbs component.
 * @param {DvtContext} context The rendering context.
 * @param {string} 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.
 * @param {object} options The object containing options specifications for this component.
 * @class
 * @constructor
 * @extends {DvtContainer}
 */
var DvtBreadcrumbs = function(context, callback, callbackObj, options) {
  this.Init(context, callback, callbackObj, options);
}

DvtObj.createSubclass(DvtBreadcrumbs, DvtContainer, "DvtBreadcrumbs");

/**
 * Initializes the component.
 * @param {DvtContext} context The rendering context.
 * @param {string} 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.
 * @param {object} options The object containing options specifications for this component.
 * @protected
 */
DvtBreadcrumbs.prototype.Init = function(context, callback, callbackObj, options) {
  DvtBreadcrumbs.superclass.Init.call(this, context);
  this.setOptions(options);
  
  // Create the event handler and add event listeners
  this._eventHandler = new DvtBreadcrumbsEventManager(this, context, callback, callbackObj);
  this._eventHandler.addListeners(this);
  
  // Make sure the object has an id for clipRect naming
  this.setId("breadcrumbs" + 1000 + Math.floor(Math.random()*10000));
}

/**
 * Specifies the non-data options for this component.
 * @param {object} options The object containing options specifications for this component.
 * @protected
 */
DvtBreadcrumbs.prototype.setOptions = function(options) {
  this._options = DvtBreadcrumbsDefaults.calcOptions(options);
}

/**
 * Renders the component with the specified data.  If no data is supplied to a component
 * that has already been rendered, the component will be re-rendered to the specified size.
 * @param {object} data The object containing data for this component.
 * @param {number} width The width of the component.
 * @param {number} height The height of the component.
 */
DvtBreadcrumbs.prototype.render = function(data, width) 
{  
  // Update if new data has been provided. Clone to avoid modifying the provided object.
  this._data = data ? DvtJSONUtils.clone(data) : this._data; 

  // Clear previous contents
  this.removeChildren();

  // Render the contents
  DvtBreadcrumbsRenderer.render(this, this, width);
  
  // Queue a render with the context
  this.getContext().queueRender();
}

/**
 * Returns the data object for the component.
 * @return {object} The object containing data for this component.
 */
DvtBreadcrumbs.prototype.__getData = function() {
  return this._data ? this._data : {};
}

/**
 * Returns the evaluated options object, which contains the user specifications
 * merged with the defaults.
 * @return {object} The options object.
 */
DvtBreadcrumbs.prototype.__getOptions = function() {
  return this._options;
}

/**
 * Returns the DvtEventManager for this component.
 * @return {DvtEventManager}
 */
DvtBreadcrumbs.prototype.__getEventManager = function() {
  return this._eventHandler;
}
/**
 * Default values and utility functions for breadcrumb versioning.
 * @class
 */
var DvtBreadcrumbsDefaults = new Object();

DvtObj.createSubclass(DvtBreadcrumbsDefaults, DvtObj, "DvtBreadcrumbsDefaults");

/**
 * Defaults for version 1.
 */ 
DvtBreadcrumbsDefaults.VERSION_1 = {
  labelStyle: "font-size: 11px; color: #003286;",
  disabledLabelStyle: "font-size: 11px;",

  //*********** Internal Attributes *************************************************//
  __labelGap: 2,
  __labelSeparator: ">"
};

/**
 * Combines the user options with the defaults for the specified version.  Returns the
 * combined options object.  This object will contain internal attribute values and
 * should be accessed in internal code only.
 * @param {object} userOptions The object containing options specifications for this component.
 * @return {object} The combined options object.
 */
DvtBreadcrumbsDefaults.calcOptions = function(userOptions) {
  var defaults = DvtBreadcrumbsDefaults._getDefaults(userOptions);
  
  // Use defaults if no overrides specified
  if(!userOptions)
    return defaults;
  else // Merge the options object with the defaults
    return DvtJSONUtils.merge(userOptions, defaults);
}

/**
 * Returns the default options object for the specified version of the component.
 * @param {object} userOptions The object containing options specifications for this component.
 * @private
 */
DvtBreadcrumbsDefaults._getDefaults = function(userOptions) {
  // Note: Version checking will eventually get added here
  // Note: Future defaults objects are deltas on top of previous objects
  return DvtJSONUtils.clone(DvtBreadcrumbsDefaults.VERSION_1);
}

/**
 * Scales down gap sizes based on the size of the component.
 * @param {object} options The object containing options specifications for this component.
 * @param {Number} defaultSize The default gap size.
 * @return {Number} 
 */
DvtBreadcrumbsDefaults.getGapSize = function(options, defaultSize) {
  return Math.ceil(defaultSize * options.layout.gapRatio);
}
/**
 * Event Manager for DvtBreadcrumbs.
 */
var DvtBreadcrumbsEventManager = function(breadcrumbs, context, callback, callbackObj) {
  this.Init(context, callback, callbackObj);
  this._breadcrumbs = breadcrumbs;
};

DvtObj.createSubclass(DvtBreadcrumbsEventManager, DvtEventManager, "DvtBreadcrumbsEventManager");

/**
 * @override
 */
DvtBreadcrumbsEventManager.prototype.OnClick = function(event) {
  DvtBreadcrumbsEventManager.superclass.OnClick.call(this, event);
  var obj = this.GetLogicalObject(event.target);
  this._processBreadcrumbs(obj);
}

/**
 * @override
 */
DvtBreadcrumbsEventManager.prototype.HandleTouchClickInternal = function(event) {
  var targetObj = event.targetObj;        
  var obj = this.GetLogicalObject(targetObj);
  this._processBreadcrumbs(obj);
}

/**
 * Processes a possible drill event on a breadcrumb.
 * @param {obj} The logical object which was clicked or tapped.
 * @private
 */
DvtBreadcrumbsEventManager.prototype._processBreadcrumbs = function(obj) {
  if(obj && obj instanceof DvtBreadcrumbsPeer && obj.isDrillable()) {
    // Create the event and fire to callbacks
    var event = new DvtBreadcrumbsDrillEvent(obj.getId());
    this.FireEvent(event, this._breadcrumbs);
  }
}
/**
 * Simple logical object for drilling and tooltip support.
 * @param {string} id The id of the associated breadcrumb.
 * @param {string} tooltip The tooltip to display.
 * @class
 * @constructor
 * @implements {DvtTooltipSource}
 */
var DvtBreadcrumbsPeer = function(id, tooltip) {
  this.Init(tooltip);
  this._id = id;
  this._bDrillable = false;
}

DvtObj.createSubclass(DvtBreadcrumbsPeer, DvtSimpleObjPeer, "DvtBreadcrumbsPeer");

/**
 * Returns the id of the associated breadcrumb.
 * @return {string}
 */
DvtBreadcrumbsPeer.prototype.getId = function() {
  return this._id;
}

/**
 * Returns true if the associated breadcrumb is drillable.
 * @return {boolean}
 */
DvtBreadcrumbsPeer.prototype.isDrillable = function() {
  return this._bDrillable;
}

/**
 * Specifies whether the associated breadcrumb is drillable.
 * @param {boolean} drillable
 */
DvtBreadcrumbsPeer.prototype.setDrillable = function(drillable) {
  this._bDrillable = drillable;
}
/**
 * Renderer for DvtBreadcrumbs.
 * @class
 */
var DvtBreadcrumbsRenderer = new Object();

DvtObj.createSubclass(DvtBreadcrumbsRenderer, DvtObj, "DvtBreadcrumbsRenderer");

/**
 * Renders the breadcrumbs in the specified area.
 * @param {DvtBreadcrumbs} breadcrumbs The breadcrumbs being rendered.
 * @param {DvtContainer} container The container to render into.
 * @param {number} width The width of the component.
 */
DvtBreadcrumbsRenderer.render = function(breadcrumbs, container, width) {
  var context = breadcrumbs.getContext();
  var dataItems = breadcrumbs.__getData().items ? breadcrumbs.__getData().items : [];
  var options = breadcrumbs.__getOptions();
  var eventManager = breadcrumbs.__getEventManager();
  
  // Create all of the labels
  var labels = [];
  var peers = [];
  for(var i=0; i<dataItems.length; i++) {
    var dataItem = dataItems[i];
    if(dataItem && dataItem.text) {
      // Create the text element
      var label = new DvtText(context, dataItem.text, 0, 0);
      var labelStyle = i < dataItems.length-1 ? options.labelStyle : options.disabledLabelStyle;
      label.setCSSStyle(new DvtCSSStyle(labelStyle));
      DvtLayoutUtils.alignTextStart(label);
      labels.push(label);
      
      // Create peer for interactivity support
      var peer = new DvtBreadcrumbsPeer(dataItem.id); 
      eventManager.associate(label, peer);
      peers.push(peer);
      
      // All except the last label are drillable
      if(i < dataItems.length-1) {
        label.setCursor("pointer");
        peer.setDrillable(true);
      }
    }
  }
  
  // Position the labels
  if(DvtStyleUtils.isLocaleR2L())
    DvtBreadcrumbsRenderer._positionLabelsBidi(breadcrumbs, container, width, labels, peers);
  else
    DvtBreadcrumbsRenderer._positionLabels(breadcrumbs, container, width, labels, peers);
}

/**
 * Positions the labels into the given container.
 * @param {DvtBreadcrumbs} breadcrumbs The breadcrumbs being rendered.
 * @param {DvtContainer} container The container in which the labels will be added.
 * @param {number} availWidth The available width for positioning.
 * @param {array} labels The array of DvtText labels.
 * @param {array} peers The array of peers corresponding to the labels.  The last label will never have a peer.
 * @private
 */
DvtBreadcrumbsRenderer._positionLabels = function(breadcrumbs, container, availWidth, labels, peers) {
  var options = breadcrumbs.__getOptions();
  var eventManager = breadcrumbs.__getEventManager();

  var x = 0;
  for(var i=0; i<labels.length; i++) {
    // Add and position the label, then calculate the space for the next one
    container.addChild(labels[i]);
    labels[i].setTranslateX(x);
    var dims = labels[i].getDimensions();
    
    // Truncate if needed
    if(x + dims.w > availWidth) {
      var labelString = labels[i].getTextString();
      labels[i].setMaxWidth(availWidth - x);
      
      // Add a tooltip
      if(i < peers.length) 
        peers[i].setTooltip(labelString);
      else 
        eventManager.associate(labels[i], new DvtSimpleObjPeer(labelString));
      
      // No more space, all done
      return;
    }
    else // Update the x
      x += dims.w + options.__labelGap;
    
    // Add a separator if there are more labels
    if(i < labels.length - 1) {
      var separator = DvtBreadcrumbsRenderer._newSeparator(breadcrumbs);
      separator.setTranslateX(x);
      container.addChild(separator);
      
      // Check that there is enough space
      var separatorWidth = separator.getDimensions().w;
      if(x + separatorWidth > availWidth) {
        container.removeChild(separator);
        return;
      }
      
      x += separatorWidth + options.__labelGap;
    }
  }
}

/**
 * Positions the labels into the given container for BIDI locales
 * @param {DvtBreadcrumbs} breadcrumbs The breadcrumbs being rendered.
 * @param {DvtContainer} container The container in which the labels will be added.
 * @param {number} availWidth The available width for positioning.
 * @param {array} labels The array of DvtText labels.
 * @param {array} peers The array of peers corresponding to the labels.  The last label will never have a peer.
 * @private
 */
DvtBreadcrumbsRenderer._positionLabelsBidi = function(breadcrumbs, container, availWidth, labels, peers) {
  var options = breadcrumbs.__getOptions();
  var eventManager = breadcrumbs.__getEventManager();

  var x = availWidth;
  for(var i=0; i<labels.length; i++) {
    // Add and position the label, then calculate the space for the next one
    container.addChild(labels[i]);
    var dims = labels[i].getDimensions();
    
    // Truncate if needed
    if(x - dims.w < 0) {
      var labelString = labels[i].getTextString();
      labels[i].setMaxWidth(x);
      labels[i].setTranslateX(0);
      
      // Add a tooltip
      if(i < peers.length) 
        peers[i].setTooltip(labelString);
      else 
        eventManager.associate(labels[i], new DvtSimpleObjPeer(labelString));
      
      // No more space, all done
      return;
    }
    else {
      // Position and update the x
      labels[i].setTranslateX(x - dims.w);
      x -= (dims.w + options.__labelGap);
    }
    
    // Add a separator if there are more labels
    if(i < labels.length - 1) {
      var separator = DvtBreadcrumbsRenderer._newSeparator(breadcrumbs);
      container.addChild(separator);
      
      // Check that there is enough space
      var separatorWidth = separator.getDimensions().w;
      if(x - separatorWidth < 0) {
        container.removeChild(separator);
        return;
      }
      else {
        // Enough space, position 
        separator.setTranslateX(x - separatorWidth);
        x -= separatorWidth + options.__labelGap;
      }
    }
  }
}

/**
 * Creates and returns a new separator for breadcrumb labels.
 * @param {DvtContext} context
 * @param {DvtBreadcrumbs} breadcrumbs The breadcrumbs being rendered.
 * @return {DvtText}
 * @private
 */
DvtBreadcrumbsRenderer._newSeparator = function(breadcrumbs) {
  var options = breadcrumbs.__getOptions();
  var label = new DvtText(breadcrumbs.getContext(), options.__labelSeparator, 0, 0);
  label.setCSSStyle(options.labelStyle);
  DvtLayoutUtils.alignTextStart(label);
  return label;
}
//TODO: remove focusEvent
/*
 * DvtComboBox ComboBox UI control.
 */
var DvtComboBox = function (context, id) {
  this.Init(context, id);
};

/*
 * make DvtComboBox a subclass of DvtContainer
 */
DvtObj.createSubclass(DvtComboBox, DvtContainer, "DvtComboBox");

DvtComboBox._HIDE_DROPDOWN_DELAY = 100;//in ms

/*--------------------------------------------------------------------*/
/*  Init()                                                            */
/*--------------------------------------------------------------------*/
DvtComboBox.prototype.Init = function (context, id) {
  DvtComboBox.superclass.Init.call(this, context, id);

  //array of button states to use for main button when given item is selected
  this._itemButtonStates = [];
  this._items = [];
  this._tooltips = [];
  this._selectedIndex = 0;

  //flag indicating if the main button states should change when
  //the selected item changes
  this._bStaticButtonStates = false;

  //BUG FIX #10142865: flag indicating that the dropdown has been hidden and
  //should be removed
  this._bRemoveDropdown = false;

  this._maxItemDim = new DvtRectangle(0, 0, 0, 0);
  this._dim = new DvtRectangle(0, 0, 0, 0);

  this._tooltipManager = context.getTooltipManager();

  //create the button representing the collapsed state
  this._button = this.createButton(id + "_cl");
  this.addChild(this._button);

  //create the content area of the button, which appears over the button 
  //itself instead of in the button so that the content doesn't
  //have to be copied for each button state
  this._contentArea = new DvtContainer(context);
  this._contentArea.setMouseEnabled(false);
  this.addChild(this._contentArea);

  //create timer to auto-hide the dropdown after focus moves out of combobox
  this._hideDropdownTimer = new DvtTimer(context, DvtComboBox._HIDE_DROPDOWN_DELAY, 
                                         this.HandleHideDropdownTimer, this, 1);
};


DvtComboBox.prototype._stopHideDropdownTimer = function() {
  if (this._hideDropdownTimer) {
    this._hideDropdownTimer.stop();
    this._hideDropdownTimer = null;
  }
};




/**
 * Get the index of the selected item.
 *
 * @return index of selected item
 */
DvtComboBox.prototype.getSelectedIndex = function () {
  return this._selectedIndex;
};

/**
 * Set the index of the selected item.
 *
 * @param selectedIndex index of item to select
 */
DvtComboBox.prototype.setSelectedIndex = function (selectedIndex) {
  this._selectedIndex = selectedIndex;

  //only update the content if the states are not static
  if (! this._bStaticButtonStates) {
    //update the content area for the selected item
    this.UpdateContentArea();
  }

  this.FireChangeEvent(selectedIndex);
};

/**
 * Create the button representing the collapsed combo box.
 *
 * @return button representing the collapsed combo box
 */
DvtComboBox.prototype.createButton = function (id) {
  var button = new DvtButton(this.getContext(), 
                             this.createUpState(id), 
                             this.createDownState(id), 
                             this.createOverState(id), 
                             id);
  button.addEventListener("click", this.HandleExpandClick, false, this);
  button.addEventListener("mousedown", this.HandleButtonDown, false, this);

  return button;
};

/**
 * Create the "up" state of the collapsed combo box button.
 *
 * @return display object representing "up" state
 */
DvtComboBox.prototype.createUpState = function (id) {
  var context = this.getContext();
  var dim = this.getDimensions();

  var s = new DvtRect(context, 0, 0, dim.w, dim.h, id + "_up");
  s.setCornerRadius(DvtButtonLAFUtils.ROUND_RECT_ELLIPSE);

  //Note: no border
  s.setFill(new DvtSolidFill(DvtButtonLAFUtils.FILL_WHITE, 0));

  s.addChild(DvtComboBox.DrawArrow(context, s.getWidth(), s.getHeight()));
  return s;
};

/**
 * Create the "over" state of the collapsed combo box button.
 *
 * @return display object representing "over" state
 */
DvtComboBox.prototype.createOverState = function (id) {
  var context = this.getContext();
  var dim = this.getDimensions();

  var s = new DvtRect(context, 0, 0, dim.w, dim.h, id + "_up");
  s.setCornerRadius(DvtButtonLAFUtils.ROUND_RECT_ELLIPSE);

  s.setStroke(new DvtSolidStroke(DvtButtonLAFUtils.BORDER_COLOR, 1, 1));
  s.setFill(new DvtGradientFill(DvtButtonLAFUtils.GRADIENT_LIGHT, 1));

  s.addChild(DvtComboBox.DrawArrow(context, s.getWidth(), s.getHeight()));
  return s;
};

/**
 * Create the "down" state of the collapsed combo box button.
 *
 * @return display object representing "down" state
 */
DvtComboBox.prototype.createDownState = function (id) {
  var context = this.getContext();
  var dim = this.getDimensions();

  var s = new DvtRect(context, 0, 0, dim.w, dim.h, id + "_up");
  s.setStroke(new DvtSolidStroke(DvtButtonLAFUtils.BORDER_COLOR, 1, 1));
  s.setFill(new DvtGradientFill(DvtButtonLAFUtils.GRADIENT_DARK, 1));
  s.setCornerRadius(DvtButtonLAFUtils.ROUND_RECT_ELLIPSE);

  s.addChild(DvtComboBox.DrawArrow(context, s.getWidth(), s.getHeight()));
  return s;
};

/**
 * Set the static button states to use for the main button.
 *
 * @param enaState enabled state
 * @param ovrState over state
 * @param dwnState down state
 *
 */
//this method is only called by panelCard
DvtComboBox.prototype.setStaticButtonStates = function (enaState, ovrState, dwnState) {
  this._button.setUpState(enaState);
  this._button.setDownState(dwnState);
  this._button.setOverState(ovrState);

  //set the flag indicating that the button states are static
  this._bStaticButtonStates = true;
};

/**
 * Add an item to the ComboBox.
 *
 * @param s displayObject to add to the ComboBox
 * @param tooltip tooltip to show for the item
 * @param enaState enabled state to use for item button in dropdown
 * @param ovrState over state to use for item button in dropdown
 * @param dwnState down state to use for item button in dropdown
 */
DvtComboBox.prototype.addItem = function (s, tooltip, enaState, ovrState, dwnState) {
  //disable mouse interaction with item
  s.setMouseEnabled(false);

  this._items.push(s);
  this._tooltips.push(tooltip);

  // get/set max item width & height
  var itdim = DvtButtonLAFUtils._getDimForced(this.getContext(), enaState);
  var maxItemDim = this._getMaxItemDim();
  var dim = this.getDimensions();
  if (itdim.w > maxItemDim.w) {
    maxItemDim.w = itdim.w;
    dim.w = itdim.w + 16;
  }
  if (itdim.h > maxItemDim.h) {
    maxItemDim.h = itdim.h;
    dim.h = itdim.h;
  }

  //store the array of states for the item button in the dropdown
  var buttonStates = null;
  if (enaState && ovrState && dwnState) {
    buttonStates = [];
    buttonStates[DvtButton.STATE_ENABLED] = enaState;
    buttonStates[DvtButton.STATE_OVER] = ovrState;
    buttonStates[DvtButton.STATE_DOWN] = dwnState;
  }
  this._itemButtonStates.push(buttonStates);

  //only create a new button and update the content if the states
  //are not static
  if (! this._bStaticButtonStates) {
    //create a new button, taking the new item's dimensions into account
    this.removeChild(this._button);
    this._button = this.createButton(s.getId());
    this.addChildAt(this._button, 0);

    //update the content area for the selected item
    this.UpdateContentArea();
  }
};

/**
 * Get an item in the ComboBox.
 *
 * @param i index of the item to get
 *
 * @return Sprite item in the ComboBox
 */
DvtComboBox.prototype.getItem = function (i) {
  return this._items[i];
};

/**
 * Get the number of items in the ComboBox.
 *
 * @return number of items in the ComboBox
 */
DvtComboBox.prototype.getItemCount = function () {
  return this._items.length;
};

/**
 * Get the index of the item in the ComboBox.
 *
 * @param s item to get index for
 *
 * @return index of the item in the ComboBox, or -1 if not found
 */
DvtComboBox.prototype.getItemIndex = function (s) {
  for (var i = 0;i < this.getItemCount();i++) {
    if (this.getItem(i) === s)
      return i;
  }
  return  - 1;
};

/**
 * Get the selected item.
 *
 * @return selected item
 */
DvtComboBox.prototype.getSelectedItem = function () {
  var selIndex = this.getSelectedIndex();
  if (selIndex < 0 || selIndex > this._items.length - 1)
    return null;

  return this._items[selIndex];
};

/**
 * Fire a change event.
 *
 * @param index index of the selected item
 *
 * @return true if event was successfully dispatched, false otherwise
 */
DvtComboBox.prototype.FireChangeEvent = function (index) {
  var comboBoxEvent = new DvtComboBoxEvent(index, this._event);
  this._event = null;
  this.FireListener(comboBoxEvent);

  return true;
};

/**
 * Handle a click on the collapsed combo box to show its dropdown.
 *
 * @param event MouseEvent
 */
DvtComboBox.prototype.HandleExpandClick = function (event) {
  //don't want click to fall through to other components
  DvtEventManager.consumeEvent(event);

  //BUG FIX #10142865: if the previous dropdown was hidden but hasn't been
  //removed yet, then remove it now before the new dropdown is created
  if (this._bRemoveDropdown) {
    this.removeDropdown();
  }

  //create the dropdown containing all the items
  this._dropdown = this.createDropdown();
  var dddim =   DvtButtonLAFUtils._getDimForced(context, this._dropdown);
  this.addChild(this._dropdown);

  var dim = this.getDimensions();

  //BiDi: in right-to-left locale, shift dropdown towards the left
  if (DvtStyleUtils.isLocaleR2L()) {
    this._dropdown.setTranslateX(5 - dddim.w);
  }
  //position the dropdown relative to the button (based on UI spec)
  else {
    this._dropdown.setTranslateX(dim.w - 5);
  }
  this._dropdown.setTranslateY(dim.h + 3);

  //disable the expand button until the dropdown is dismissed
  this._button.setMouseEnabled(false);

  //add a mouse focus change listener to the stage to detect
  //when we should auto-hide the dropdown
  var stage = this.getContext().getStage();
  if (stage) {
    // stage.addEventListener(FocusEvent.MOUSE_FOCUS_CHANGE, this.HandleStageMouseFocusChange, false, this);
  }
};

/**
 * Handle an event from the timer used to auto-hide the dropdown.
 *
 * @param event TimerEvent
 */
DvtComboBox.prototype.HandleHideDropdownTimer = function (event) {
  //hide the dropdown
  this.HideDropdown();

  //only update the content if the button states are not static
  if (! this._bStaticButtonStates) {
    //update the content area to show the currently selected item
    this.UpdateContentArea();
  }
};

/**
 * Handle a mouse focus change event on the stage.
 *
 * @param even FocusEvent
 */
DvtComboBox.prototype.HandleStageMouseFocusChange = function (event) {
  //determine if the focus has moved out of the combo box;
  //if so, hide the dropdown
  var relatedObject = event.relatedObject;
  while (relatedObject) {
    if (relatedObject === this)
      break;
    relatedObject = relatedObject.parent;
  }
  if (! relatedObject) {
    //reset and start the timer to auto-hide the dropdown;
    //the timer will be stopped if an item in the dropdown was selected
    this._hideDropdownTimer.reset();
    this._hideDropdownTimer.start();
  }
};

/**
 * Create the dropdown showing all the available items to select.
 *
 * @return display object representing the dropdown
 */
DvtComboBox.prototype.createDropdown = function () {
  //constants for padding around items in dropdown
  var topPadding = 2;
  var leftPadding = 3;
  var bottomPadding = 6;
  var rightPadding = 3;
  var interItemPadding = 2;
  var currY = topPadding;
  var context = this.getContext();
  var dim;
  var itemSprite;
  var item;

  var content = new DvtContainer(context, "__dd");
  for (var i = 0; i < this._items.length; i++) {
    item = this._items[i];
    dim = DvtButtonLAFUtils._getDimForced(context, item);

    //move each item below the previous one
    itemSprite = new DvtContainer(context, "__it" + i);
    itemSprite.setTranslateX(leftPadding);
    itemSprite.setTranslateY(currY);

    itemSprite.addChild(item);
    item.setMouseEnabled(true);

    currY += dim.h + interItemPadding;

    //add event listeners to each item
    item.addEventListener("click", this.HandleDropdownItemClick, false, this);
    item.addEventListener("mousedown", this.HandleDropdownItemDown, false, this);
    item.addEventListener("mouseover", this.HandleDropdownItemRollOver, false, this);
    item.addEventListener("mouseout", this.HandleDropdownItemRollOut, false, this);

    content.addChild(itemSprite);
  }

  var dd = DvtButtonLAFUtils.drawDropdownShape(context, 
            this._getMaxItemDim().w + leftPadding + rightPadding,
            currY + bottomPadding - interItemPadding);;
  dd.addChild(content);

  //draw the background behind all the items now that we know the 
  //size of the dropdown
  dd.setStroke(new DvtSolidStroke(DvtButtonLAFUtils.BORDER_WHITE, .7, 1));
  dd.setFill(new DvtSolidFill(DvtButtonLAFUtils.FILL_WHITE, .2));

  return dd;
};


/**
 * Handle a mouse down
 *
 * @param event MouseEvent
 */
DvtComboBox.prototype.HandleButtonDown = function (event) {
  // logEvent(event, "DvtCombo");
  DvtEventManager.consumeEvent();
}

/**
 * Handle a mouse rollover on an item in the dropdown.
 *
 * @param event MouseEvent
 */
DvtComboBox.prototype.HandleDropdownItemRollOver = function (event) {
  // logEvent(event, "DvtCombo");

  //don't want click to fall through to other components
  DvtEventManager.consumeEvent(event);

  var s = event.target;

  //highlighting is now Handled by the item itself
  //show a tooltip for this item if possible
  if (this._tooltipManager) {
    var index = this.getItemIndex(s.getChildAt(0));
    if (index >  - 1) {
      var tooltip = this._tooltips[index];
      if (tooltip && tooltip.length > 0)
        this._tooltipManager.showTooltip(tooltip, s);
    }
  }
};

/**
 * Handle a mouse rollout on an item in the dropdown.
 *
 * @param event MouseEvent
 */
DvtComboBox.prototype.HandleDropdownItemRollOut = function (event) {
  // logEvent(event, "DvtCombo");

  //don't want click to fall through to other components
  DvtEventManager.consumeEvent(event);

  //highlighting is now Handled by the item itself
  //hide the tooltip for this item
  if (this._tooltipManager) {
    this._tooltipManager.hideTooltip();
  }

  //BUG FIX #10142865: if the dropdown was hidden but hasn't been
  //removed yet, then remove it now after the mouse has rolled out
  //so that the button that the mouse was over will go back to 
  //its default state
  if (this._bRemoveDropdown)
    this.RemoveDropdown();
};

/**
 * Handle a mouse click on an item in the dropdown.
 *
 * @param event MouseEvent
 */
DvtComboBox.prototype.HandleDropdownItemClick = function (event) {
  // logEvent(event, "DvtCombo");

  //stop the dropdown auto-hide timer
  this._hideDropdownTimer.stop();
//   this._hideDropdownTimer = null;

  //don't want click to fall through to other components
  DvtEventManager.consumeEvent(event);

  //update after the event so that animation is smooth
  // event.updateAfterEvent();

  //hide the dropdown and set the new selected item index
  //get the root sprite of the item
  var eventTarget = DvtComboBox.getRootDropdownItemSprite(event.target);

  var index;
  //get the index of the item
  if (this._dropdown && eventTarget) {
    var content = this._dropdown.getChildAt(0);
    if (content) {
      index = content.getChildIndex(eventTarget);
    }
  }

  this.HideDropdown();

  //Note: this._event is clear after firing
  this._event = event;

  this.setSelectedIndex(index);

};

/**
 * Get the root DropdownItemSprite of the given object in the dropdown.
 *
 * @param displayObject display object in an item in the dropdown
 *
 * @return root DropdownItemSprite parent for the object
 */
DvtComboBox.getRootDropdownItemSprite = function (displayObject) {
  var n;
  var id;
  while (displayObject) {
    id = displayObject.getId();
    if (id && displayObject instanceof DvtContainer && id.substr(0, 4) == "__it" ) {
      n = displayObject;
      break;
    }
    displayObject = displayObject.getParent();
  }

  return n;
};

/**
 * Hide the dropdown and enable the main button again.
 */
DvtComboBox.prototype.HideDropdown = function () {
  if (this._dropdown) {
    this._dropdown.setVisible(false);

    //BUG FIX #10142865: don't remove the dropdown yet, because we want 
    //the item the mouse was over to register a mouse rollout event
    //first so that when we next show the dropdown, which will contain
    //the same item, the item won't appear to be highlighted at first;
    //instead, just set the flag to remove the dropdown later
    //this._dropdown.parent.removeChild(this._dropdown);
    //this._dropdown = null;
    this._bRemoveDropdown = true;
  }

  //enable expand button again
  this._button.setMouseEnabled(true);

  //remove the mouse focus change listener from the stage now
  //that the dropdown is no longer visible
  //bug 9885226: ACC:keyBoard togggle for show/hide controlPanel is borken
  var stage = this.getContext().getStage();
  if (stage) {
    // stage.removeEventListener(FocusEvent.MOUSE_FOCUS_CHANGE, 
    //                           this.HandlenStageMouseFocusChange, false);
  }
};

/**
 * Remove the dropdown from the display list.
 */
DvtComboBox.prototype.RemoveDropdown = function () {
  //BUG FIX #10142865: remove the dropdown from the display list after
  //the item in it has had time to register a mouse rollout event
  //so that it doesn't appear to be highlighted the next time the
  //dropdown is displayed
  //turn off the flag because we're removing the dropdown now
  this._bRemoveDropdown = false;
  if (this._dropdown) {
    this._dropdown.setVisible(false);

    for (var i = 0;i < this._items.length;i++) {
      var itemSprite = this._items[i];
      itemSprite.removeEventListener("click", this.HandleDropdownItemClick);
      itemSprite.removeEventListener("mouseover", this.HandleDropdownItemRollOver);
      itemSprite.removeEventListener("mouseout", this.HandleDropdownItemRollOut);
      itemSprite.removeEventListener("mousedown", this.HandleButtonDown);
    }

    this._dropdown.getParent().removeChild(this._dropdown);
    this._dropdown = null;
  }
};

/**
 * Get the maximum size of the items.
 *
 * @return maximum size of the items
 */
DvtComboBox.prototype._getMaxItemDim = function () {
  return this._maxItemDim;
};

/**
 * @override
 */
DvtComboBox.prototype.getDimensions = function () {
  return this._dim;
};

/**
 * Update the content area of the collapsed combo box to
 * show the selected item.
 */
DvtComboBox.prototype.UpdateContentArea = function () {
  //flag indicating if the content should re-use the item itself
  var bUseItem = true;

  //if the states for the main button are specified by the item,
  //then use them
  var selIndex = this.getSelectedIndex();
  if (this._itemButtonStates && this._itemButtonStates.length > selIndex) {

    var buttonStates = this._itemButtonStates[selIndex];
    if (buttonStates && buttonStates.length > 2) {
      this._button.setUpState(buttonStates[DvtButton.STATE_ENABLED]);
      this._button.setOverState(buttonStates[DvtButton.STATE_OVER]);
      this._button.setDownState(buttonStates[DvtButton.STATE_DOWN]);

      //toggle the flag because the button states are specified
      bUseItem = false;
    }
  }

  //if using the item itself in the button, update the content with it
  if (bUseItem) {
    //remove old content
    if (this._contentArea.getNumChildren () > 0) {
      this._contentArea.removeChildAt(0);
    }
    //add currently selected item
    this._contentArea.addChild(this.getSelectedItem());
  }
};

/**
 * Draw the arrow in the collapsed combo box button.
 *
 * @param s Sprite in which to draw arrow
 */
DvtComboBox.DrawArrow = function (context, ww, hh) {
  var padding = 4;

  var aPoints = [];
  aPoints.push(ww - 12, hh / 2 - padding / 2);
  aPoints.push(ww - 4, hh / 2 - padding / 2);
  aPoints.push(ww - 8, hh / 2 + padding / 2);
  aPoints.push(ww - 12, hh / 2 - padding / 2);

  var shape = new DvtPolygon(context, aPoints, null);
  shape.setStroke(new DvtSolidStroke(DvtButtonLAFUtils.BORDER_COLOR, 1, 1));
  shape.setFill(new DvtSolidFill(DvtButtonLAFUtils.BORDER_COLOR, 1));
  shape.setMouseEnabled(false);

  return shape;
};

/**
 * Hide the dropdown and enable the main button again.
 */
DvtComboBox.prototype.closeDropdown = function () {
  this.HideDropdown();
};


/*
 * DropdownItemSprite Class to use as item in dropdown.
 */
var DropdownItemSprite = function (context, x, y, w, h, id) {
  this.Init(context, x, y, w, h, id);
};

/*
 * make DropdownItemSprite a subclass of DvtRect
 */
DvtObj.createSubclass(DropdownItemSprite, DvtRect, "DropdownItemSprite");

DropdownItemSprite.prototype.Init = function (context, x, y, w, h, id) {
  DvtRect.superclass.Init.call(this, context);
};



/**
 * Base Class for PanZoomControl
 */
var DvtBasePanZoomControl = function (context, panZoomCanvas, resources) {
  this.Init(context, panZoomCanvas, resources);
};


/*
 * make DvtBasePanZoomControl a subclass of DvtContainer
 */
DvtObj.createSubclass(DvtBasePanZoomControl, DvtContainer, "DvtBasePanZoomControl");

/*--------------------------------------------------------------------*/
/*  Init()                                                            */
/*--------------------------------------------------------------------*/
DvtBasePanZoomControl.prototype.Init = function (context, panZoomCanvas, resources) {
  DvtBasePanZoomControl.superclass.Init.call(this, context);

  this._panZoomCanvas = panZoomCanvas;
  this._context = context;
  this._tooltipManager = context.getTooltipManager();
  this._resources = resources;
}
    
/**
 * Set the tooltip manager to use.
 * 
 * @param tooltipManager manager to use
 */
DvtBasePanZoomControl.prototype.setTooltipManager = function (tooltipManager) {
  this._tooltipManager = tooltipManager;
}
  
/**
 * Get the tooltip manager being used.
 * 
 * @return tooltipManager being used
 */
DvtBasePanZoomControl.prototype.getTooltipManager = function () {
  return this._tooltipManager;
}
    
/**
 * Set the translated resources to use.
 * 
 * @param resources translated resources
 */
DvtBasePanZoomControl.prototype.setResources = function (resources) {
  this._resources = resources;
}
  
/**
 * Get the translated resources to use.
 * 
 * @return translated resources
 */
DvtBasePanZoomControl.prototype.getResources = function () {
  if (this._resources == null) {
    this._resources = new DvtPanZoomControlPanelResources();
  }
  return this._resources;
}



/**
 * Show tooltip
 * 
 * @return tooltip text to display
 */
DvtBasePanZoomControl.prototype.ShowTooltip = function (event, tooltip) {
  this.getTooltipManager().showTooltip(event.pageX, event.pageY, 
                                       tooltip, event.target);
}
    
/**
 * Hide tooltip
 */
DvtBasePanZoomControl.prototype.HideTooltip = function () {
  this.getTooltipManager().hideTooltip();
}
    

DvtBasePanZoomControl.prototype._logEvent = function (event) {
//  var name = this.getTypeName();
//  if (name === "DvtPanControl") {
//    logEvent(event, name);
//  }
}
    

/**
 * ZoomInButton for use with Diagram.
 */
var DvtZoomInButton = function (context, button, panZoomCanvas, resources) {
  this.Init(context, button, panZoomCanvas, resources);
};

DvtZoomInButton._ZOOM_INCR_TIMER_INTERVAL = 100;//in ms
DvtZoomInButton._ZOOM_START_TIMER_INTERVAL = 250;//in ms

/*
 * make DvtZoomInButton a subclass of DvtBasePanZoomControl
 */
DvtObj.createSubclass(DvtZoomInButton, DvtBasePanZoomControl, "DvtZoomInButton");

/*--------------------------------------------------------------------*/
/*  Init()                                                            */
/*--------------------------------------------------------------------*/
DvtZoomInButton.prototype.Init = function (context, button, panZoomCanvas, resources) {
  DvtZoomInButton.superclass.Init.call(this, context, panZoomCanvas, resources);

  this._bZoomInMouseDown = false;
  this._zoomInTimer = new DvtTimer(context, DvtZoomInButton._ZOOM_INCR_TIMER_INTERVAL,
                                   this.HandleZoomInTimer, this);

  //BUG FIX #10037201: delay the start of the zoom in timer so that there is
  //less chance that it will interfere with a slow, regular click
  this._zoomStartTimer = new DvtTimer(context, DvtZoomInButton._ZOOM_START_TIMER_INTERVAL, 
                                      this.HandleZoomStartTimer, this, 1);

  this._zoomInButton = button;
  this._zoomInButton.addEventListener("click", this.HandleZoomInClick, false, this);
  this._zoomInButton.addEventListener("mousedown", this.HandleZoomInMouseDown, false, this);
  this._zoomInButton.addEventListener("mouseup", this.HandleZoomInMouseUp, false, this);
  this._zoomInButton.addEventListener("mouseover", this.HandleZoomInRollOver, false, this);
  this._zoomInButton.addEventListener("mouseout", this.HandleZoomInMouseUp, false, this);
  
  this.addChild(this._zoomInButton);
}

/**
 *this.Handle a mouse click on the zoom in button.
 *
 * @param event MouseEvent
 */
DvtZoomInButton.prototype.HandleZoomInClick = function (event) {
  //don't want click to fall through to other components
  DvtEventManager.consumeEvent(event);
  this._logEvent(event);

  //don't respond to click event if the timer had been 
  //continuously zooming
  if (this._zoomInTimer.isRunning()) {
    return;
  }
  var currZoom = this._panZoomCanvas.getZoom();
  var newZoom = this._panZoomCanvas.getNextZoomLevel(currZoom);
  //trace("zoomIn: " + currZoom + " to " + newZoom);

  var animator = new DvtAnimator(this.getContext(), .5);
  this._panZoomCanvas.zoomTo(newZoom, undefined, undefined, animator);
  animator.play();
}

/**
 *this.Handle a mouse down on the zoom in button.
 *
 * @param event MouseEvent
 */
DvtZoomInButton.prototype.HandleZoomInMouseDown = function (event) {
  //don't want click to fall through to other components
  DvtEventManager.consumeEvent(event);
  this._logEvent(event);

  this._bZoomInMouseDown = true;

  //reset the timers and start the initial delay timer
  //TODO   this._zoomInTimer.reset();
  this._zoomInTimer.stop();

  //BUG FIX #10037201: delay the start of the zoom in timer so that there is
  //less chance that it will interfere with a slow, regular click
  //TODO   this._zoomStartTimer.reset();
  this._zoomStartTimer.stop();
  this._zoomStartTimer.start();
}

/**
 *this.Handle a mouse up on the zoom in button.
 *
 * @param event MouseEvent
 */
DvtZoomInButton.prototype.HandleZoomInMouseUp = function (event) {
  if (this._bZoomInMouseDown) {

    //don't want click to fall through to other components
    DvtEventManager.consumeEvent(event);
    this._logEvent(event);

    this._bZoomInMouseDown = false;

    //stop the timers
    this._zoomInTimer.stop();
    this._zoomStartTimer.stop();
  }

  this.HideTooltip();

}

/**
 *this.Handle a mouse roll over on the zoom in button.
 *
 * @param event MouseEvent
 */
DvtZoomInButton.prototype.HandleZoomInRollOver = function (event) {
  //Note: dont consume event here
//   DvtEventManager.consumeEvent(event);
  this._logEvent(event);

  this.ShowTooltip(event, this.getResources().getZoomInTooltip());
}

/**
 *this.Handle an event from the start timer.
 *
 * @param event TimerEvent
 */
DvtZoomInButton.prototype.HandleZoomStartTimer = function (event) {
  //start the zoom in timer
  this._zoomInTimer.start();
}

/**
 *this.Handle a zoom in timer event to zoom in on the view.
 *
 * @param event TimerEvent
 */
DvtZoomInButton.prototype.HandleZoomInTimer = function (event) {
  var currZoom = this._panZoomCanvas.getZoom();
  var newZoom = this._panZoomCanvas.getNextZoomLevel(currZoom);

  this._panZoomCanvas.zoomTo(newZoom);
}

DvtZoomInButton.prototype._stopTimer = function() {
  if (this._zoomInTimer) {
    this._zoomInTimer.stop();
    this._zoomInTimer = null;
  }
  if (this._zoomStartTimer) {
    this._zoomStartTimer.stop();
    this._zoomStartTimer = null;
  }
};



/**
 * ZoomOutButton for use with Diagram.
 */
var DvtZoomOutButton = function (context, button, panZoomCanvas, resources) {
  this.Init(context, button, panZoomCanvas, resources);
};

DvtZoomOutButton._ZOOM_INCR_TIMER_INTERVAL = 100;//in ms
DvtZoomOutButton._ZOOM_START_TIMER_INTERVAL = 250;//in ms

/*
 * make DvtZoomOutButton a subclass of DvtBasePanZoomControl
 */
DvtObj.createSubclass(DvtZoomOutButton, DvtBasePanZoomControl, "DvtZoomOutButton");

/*--------------------------------------------------------------------*/
/*  Init()                                                            */
/*--------------------------------------------------------------------*/
DvtZoomOutButton.prototype.Init = function (context, button, panZoomCanvas, resources) {
  DvtZoomOutButton.superclass.Init.call(this, context, panZoomCanvas, resources);
  this._bZoomOutMouseDown = false;
  this._zoomOutTimer = new DvtTimer(context, DvtZoomOutButton._ZOOM_INCR_TIMER_INTERVAL,
                                    this.HandleZoomOutTimer, this);

  //BUG FIX #10037201: delay the start of the zoom out timer so that there is
  //less chance that it will interfere with a slow, regular click
  this._zoomStartTimer = new DvtTimer(context, DvtZoomOutButton._ZOOM_START_TIMER_INTERVAL, 
                                      this.HandleZoomStartTimer, this, 1);
  this._zoomOutButton = button;
  this._zoomOutButton.addEventListener("click", this.HandleZoomOutClick, false, this);
  this._zoomOutButton.addEventListener("mousedown", this.HandleZoomOutMouseDown, false, this);
  this._zoomOutButton.addEventListener("mouseup", this.HandleZoomOutMouseUp, false, this);
  this._zoomOutButton.addEventListener("mouseover", this.HandleZoomOutRollOver, false, this);
  this._zoomOutButton.addEventListener("mouseout", this.HandleZoomOutMouseUp, false, this);
  this.addChild(this._zoomOutButton);
}

/**
 *this.Handle a mouse click on the zoom out button.
 *
 * @param event MouseEvent
 */
DvtZoomOutButton.prototype.HandleZoomOutClick = function (event) {
  //don't want click to fall through to other components
  DvtEventManager.consumeEvent(event);
  this._logEvent(event);

  //don't respond to click event if the timer had been 
  //continuously zooming
  if (this._zoomOutTimer.isRunning()) {
    return;
  }
  var currZoom = this._panZoomCanvas.getZoom();
  var newZoom = this._panZoomCanvas.getPrevZoomLevel(currZoom);
  //trace("zoomOut: " + currZoom + " to " + newZoom);

  var context = this.getContext();
  var animator = new DvtAnimator(context, .5);

  this._panZoomCanvas.zoomTo(newZoom, undefined, undefined, animator);

  animator.play();
}

/**
 *this.Handle a mouse down on the zoom out button.
 *
 * @param event MouseEvent
 */
DvtZoomOutButton.prototype.HandleZoomOutMouseDown = function (event) {
  //don't want click to fall through to other components
  DvtEventManager.consumeEvent(event);
  this._logEvent(event);

  this._bZoomOutMouseDown = true;

  //reset the timers and start the initial delay timer
  //   this._zoomOutTimer.reset();
  this._zoomOutTimer.stop();

  //BUG FIX #10037201: delay the start of the zoom out timer so that there is
  //less chance that it will interfere with a slow, regular click
  //   this._zoomStartTimer.reset();
  this._zoomStartTimer.stop();
  this._zoomStartTimer.start();
}

/**
 *this.Handle a mouse up on the zoom out button.
 *
 * @param event MouseEvent
 */
DvtZoomOutButton.prototype.HandleZoomOutMouseUp = function (event) {
  if (this._bZoomOutMouseDown) {
    //don't want click to fall through to other components
    DvtEventManager.consumeEvent(event);
    this._logEvent(event);

    this._bZoomOutMouseDown = false;

    //stop the timers
    this._zoomOutTimer.stop();
    this._zoomStartTimer.stop();
  }

  this.HideTooltip();
}

/**
 *this.Handle a mouse roll over on the zoom out button.
 *
 * @param event MouseEvent
 */
DvtZoomOutButton.prototype.HandleZoomOutRollOver = function (event) {
  //Note: dont consume event here
//   DvtEventManager.consumeEvent(event);
  this._logEvent(event);

  this.ShowTooltip(event, this.getResources().getZoomOutTooltip());
}

/**
 *this.Handle an event from the start timer.
 *
 * @param event TimerEvent
 */
DvtZoomOutButton.prototype.HandleZoomStartTimer = function (event) {
  //start the zoom out timer
  this._zoomOutTimer.start();
}

/**
 *this.Handle a zoom out timer event to zoom out on the view.
 *
 * @param event TimerEvent
 */
DvtZoomOutButton.prototype.HandleZoomOutTimer = function (event) {
  var currZoom = this._panZoomCanvas.getZoom();
  var newZoom = this._panZoomCanvas.getPrevZoomLevel(currZoom);

  this._panZoomCanvas.zoomTo(newZoom);
}

DvtZoomOutButton.prototype._stopTimer = function() {
  if (this._zoomOutTimer) {
    this._zoomOutTimer.stop();
    this._zoomOutTimer = null;
  }
  if (this._zoomStartTimer) {
    this._zoomStartTimer.stop();
    this._zoomStartTimer = null;
  }
};



/**
 * ZoomToFitButton for use with Diagram.
 */
var DvtZoomToFitButton = function (context, panZoomCanvas, resources) {
  this.Init(context, panZoomCanvas, resources);
};

/*
 * make DvtZoomToFitButton a subclass of DvtBasePanZoomControl
 */
DvtObj.createSubclass(DvtZoomToFitButton, DvtBasePanZoomControl, "DvtZoomToFitButton");

/*--------------------------------------------------------------------*/
/*  Init()                                                            */
/*--------------------------------------------------------------------*/
DvtZoomToFitButton.prototype.Init = function (context, button, panZoomCanvas, resources) {
  DvtZoomToFitButton.superclass.Init.call(this, context, panZoomCanvas, resources);

  this._zoomToFitButton = button;
  button.addEventListener("click", this.HandleZoomToFitClick, false, this);
  button.addEventListener("mousedown", this.HandleZoomToFitDown, false, this);
  button.addEventListener("mouseup", this.HandleZoomToFitUp, false, this);
  button.addEventListener("mouseover", this.HandleZoomToFitRollOver, false, this);
  button.addEventListener("mouseout", this.HandleZoomToFitUp, false, this);
  this.addChild(this._zoomToFitButton);
}

/**
 * Handle a mouse click on the zoom to fit button.
 *
 * @param event MouseEvent
 */
DvtZoomToFitButton.prototype.HandleZoomToFitClick = function (event) {
  //don't want click to fall through to other components
  DvtEventManager.consumeEvent(event);
  this._logEvent(event);

  var animator = new DvtAnimator(this.getContext(), .5);
  this._panZoomCanvas.zoomToFit(animator);
  animator.play();
};

/**
 * Handle a mouse down on the zoom to fit button.
 *
 * @param event MouseEvent
 */
DvtZoomToFitButton.prototype.HandleZoomToFitDown = function (event) {
  DvtEventManager.consumeEvent(event);
  this._logEvent(event);
};

/**
 * Handle a mouse roll out on the zoom to fit button.
 *
 * @param event MouseEvent
 */
DvtZoomToFitButton.prototype.HandleZoomToFitUp = function (event) {
  DvtEventManager.consumeEvent(event);
  this._logEvent(event);

  this.HideTooltip();
};

/**
 * Handle a mouse roll over on the zoom to fit button.
 *
 * @param event MouseEvent
 */
DvtZoomToFitButton.prototype.HandleZoomToFitRollOver = function (event) {
  //Note: dont consume event here
//   DvtEventManager.consumeEvent(event);
  this._logEvent(event);

  this.ShowTooltip(event, this.getResources().getZoomToFitTooltip());
};



/**
 * ZoomSlider for use with Diagram.
 */
var DvtZoomSlider = function (context, panZoomCanvas, resources, zoomLevels, zoomLevelsTooltips, minScale, maxScale) {
  this.Init(context, panZoomCanvas, resources, zoomLevels, zoomLevelsTooltips, minScale, maxScale);
};

DvtZoomSlider.DEFAULT_ZOOM_LEVELS = [0, .25, .5, .75, 1];
DvtZoomSlider.ZOOM_SLIDER_HEIGHT = 76;
DvtZoomSlider.ZOOM_SLIDER_WIDTH = 15;

/*
 * make DvtZoomSlider a subclass of DvtBasePanZoomControl
 */
DvtObj.createSubclass(DvtZoomSlider, DvtBasePanZoomControl, "DvtZoomSlider");

/*--------------------------------------------------------------------*/
/*  Init()                                                            */
/*--------------------------------------------------------------------*/
DvtZoomSlider.prototype.Init = function (context, panZoomCanvas, resources, zoomLevels, zoomLevelsTooltips, minScale, maxScale) {
  DvtZoomSlider.superclass.Init.call(this, context, panZoomCanvas, resources);

  this._zoomLevels = DvtZoomSlider.DEFAULT_ZOOM_LEVELS;
  this._bZoomSliderThumbMouseDown = false;
  this._panZoomCanvas.addEventListener(ZoomEvent.ZOOM, this.HandleZoomEvent, false, this);

  if (!zoomLevels)
    zoomLevels = DvtZoomSlider.DEFAULT_ZOOM_LEVELS;
  this._zoomLevels = zoomLevels;

  this._zoomLevelsTooltips = zoomLevelsTooltips;
  if (!minScale)
    minScale = 0;
  if (!maxScale)
    maxScale = 1;
  this._minScale = minScale;
  this._maxScale = maxScale;

  this.Render();

}

DvtZoomSlider.prototype.getZoomLevels = function () {
  return this._zoomLevels;
}

/**
 * Set the tooltips for the discrete zoom levels in the zoom slider.
 *
 * @param zoomLevelsTooltips tooltips for discrete zoom levels in zoom slider
 */
DvtZoomSlider.prototype.setZoomLevelsTooltips = function (zoomLevelsTooltips) {
  this._zoomLevelsTooltips = zoomLevelsTooltips;

  this.AddZoomSliderCrosslinesTooltipListeners();
}

/**
 * Get the tooltips for the discrete zoom levels in the zoom slider.
 *
 * @return tooltips for discrete zoom levels in zoom slider
 */
DvtZoomSlider.prototype.getZoomLevelsTooltips = function () {
  return this._zoomLevelsTooltips;
}

DvtZoomSlider.prototype.AddZoomSliderCrosslinesTooltipListeners = function () {
  var zoomLevelsTooltips = this.getZoomLevelsTooltips();
  var numZoomLevelsTooltips = 0;
  if (zoomLevelsTooltips)
    numZoomLevelsTooltips = zoomLevelsTooltips.length;

  var crossline;
  var zoomLevelTooltip;
  var numCrosslines = 0;
  if (this._zoomSliderCrosslines)
    numCrosslines = this._zoomSliderCrosslines.length;

  for (var i = 0;i < numCrosslines;i++) {
    if (i < numZoomLevelsTooltips) {
      crossline = this._zoomSliderCrosslines[i];
      zoomLevelTooltip = zoomLevelsTooltips[i];
      if (zoomLevelTooltip != null && zoomLevelTooltip.length > 0) {
        if (!crossline.hasEventListener("mouseover")) {
          crossline.addEventListener("mouseover", this.HandleZoomSliderCrosslineRollOver, false, 0, true);
        }
        if (!crossline.hasEventListener("mouseout")) {
          crossline.addEventListener("mouseout", this.HandleZoomSliderCrosslineRollOut, false, 0, true);
        }
      }
    }
  }
}

/**
 * Create the zoom slider.
 *
 * @return zoom slider
 */
DvtZoomSlider.prototype.Render = function () {
  this._zoomSliderCrosslines = [];

  var zoomLevels = this.getZoomLevels();
  var numZoomLevels = 0;
  if (zoomLevels != null)
    numZoomLevels = zoomLevels.length;

  var ww = ZOOM_SLIDER_WIDTH;
  var gap = NaN;
  if (numZoomLevels >= 0)
    gap = ZOOM_SLIDER_HEIGHT / 5;
  var padding = 4;

  //set the size of the slider
  var g = graphics;
  g.lineStyle(0, 0, 0);
  g.DvtPathUtils.moveTo(0, 0);
  g.DvtPathUtils.lineTo(ww, ZOOM_SLIDER_HEIGHT);

  var lineThickness = 1;//2;
  var mouseLineThickness = 5;

  this._zoomSliderStartY = 2 * padding;
  this._zoomSliderEndY = height - 2 * padding;

  //draw the incremental cross lines
  var crossline;
  for (var i = 0;i < numZoomLevels;i++) {
    crossline = new Sprite();
    crossline.buttonMode = true;
    var intersectY = this._zoomSliderEndY - ((zoomLevels[i] - this._minScale) / (this._maxScale - this._minScale)) * (this._zoomSliderEndY - this._zoomSliderStartY);
    crossline.y = intersectY;
    addChild(crossline);
    g = crossline.graphics;
    PanZoomDvtButtonLAFUtilsPanelLAFUtils.drawZoomSliderCrossLine(crossline, padding, 0, ww);
    //BUG #6977287: increase mouse detection region
    g.lineStyle(mouseLineThickness, 0, 0);
    g.DvtPathUtils.moveTo(padding, 0);
    g.DvtPathUtils.lineTo(padding + ww, 0);

    this._zoomSliderCrosslines.push(crossline);

    crossline.addEventListener("click", this.HandleZoomSliderCrosslineClick, false, 0, true);
  }

  this.addZoomSliderCrosslinesTooltipListeners();

  //create the thumb
  this._zoomSliderThumb = this._createZoomSliderThumb();
  this._positionZoomSliderThumb();

  this.addChild(this._zoomSliderThumb);
}

/**
 * Create the zoom slider thumb.
 *
 * @return zoom slider thumb
 */
DvtZoomSlider.prototype._createZoomSliderThumb = function () {
  var padding = 4;
  var context = this.getContext();
  var s = new DvtContainer(context);

  // Bug 9506699 - BIDI: Control panel buttons should have local specific icons
  // in case of R2L, zoomslider button should start from right
  var ww = DvtStyleUtils.isLocaleL2R() ? 0 : (ZOOM_SLIDER_WIDTH - 2);

  var button = new DvtButton(context,
                   DvtButtonLAFUtils.createZoomSliderButtonState(context, DvtImageLAFUtils.STATE_ENABLED, ww),
                   DvtButtonLAFUtils.createZoomSliderButtonState(DvtImageLAFUtils.STATE_OVER, ww),
                   DvtButtonLAFUtils.createZoomSliderButtonState(DvtImageLAFUtils.STATE_DOWN, ww));
  s.addChild(button);

  s.addEventListener("mousedown", this.HandleZoomSliderThumbMouseDown, false, this);
  //BUG FIX #10079178: add mouseMove and mouseUp listeners when we get the mouseDown event
  //s.addEventListener("mousemove", this.HandleZoomSliderThumbMouseMove, false, this);
  //s.addEventListener("mouseup", this.HandleZoomSliderThumbMouseUp, false, this);
  //s.addEventListener("mouseout", this.HandleZoomSliderThumbMouseUp, false, this);
  s.addEventListener("click", DvtEventManager.consumeEvent, false, this);

  return s;
}

/**
 * Position the zoom slider thumb based on the current zoom factor.
 */
DvtZoomSlider.prototype._positionZoomSliderThumb = function () {
  if (this._panZoomCanvas && this._zoomSliderThumb) {
    var currZoom = this._panZoomCanvas.getCurrentZoom();

    var yPos = Number(this._zoomSliderEndY) - ((currZoom - this._minScale) / (this._maxScale - this._minScale)) * Number(this._zoomSliderEndY - this._zoomSliderStartY);

    if (yPos > this._zoomSliderEndY)
      yPos = this._zoomSliderEndY;
    else if (yPos < this._zoomSliderStartY)
      yPos = this._zoomSliderStartY;
    this._zoomSliderThumb.y = yPos - (this._zoomSliderThumb.height / 2.0);
  }
}

/**
 *this.Handle a zoom event from the view.
 * The UI should update for the new zoom state.
 *
 * @param event zoom event
 */
DvtZoomSlider.prototype.HandleZoomEvent = function (event) {
  if (event.getSubType() == ZoomEvent.SUBTYPE_ZOOMED) {
    this._positionZoomSliderThumb();
  }
}

/**
 *this.Handle a mouse click on the continuous zoom slider line.
 *
 * @param event MouseEvent
 */
DvtZoomSlider.prototype.HandleZoomSliderBodyClick = function (event) {
  //don't want click to fall through to other components
  DvtEventManager.consumeEvent(event);

  var relY = this._zoomSliderEndY - event.localY;
  if (relY < 0)
    relY = 0;
  var ratio = this._minScale + (relY / (this._zoomSliderEndY - this._zoomSliderStartY)) * (this._maxScale - this._minScale);

  var animator = new DvtAnimator(this.getContext(), .5);
  this._panZoomCanvas.zoomTo(ratio, undefined, undefined, animator);
  animator.play();
}

/**
 *this.Handle a mouse click on one of the incremental
 * cross lines on the zoom slider.
 *
 * @param event MouseEvent
 */
DvtZoomSlider.prototype.HandleZoomSliderCrosslineClick = function (event) {
  //don't want click to fall through to other components
  DvtEventManager.consumeEvent(event);

  var i;
  var numCrosslines = 0;
  if (this._zoomSliderCrosslines != null)
    numCrosslines = this._zoomSliderCrosslines.length;
  for (i = 0;i < numCrosslines;i++) {
    //find the crossline that was clicked, so we know the index,
    //which is the same as the index of its zoom level in the array
    if (event.target == this._zoomSliderCrosslines[i])
      break;
  }
  var newScale = this._zoomLevels[i];

  var animator = new DvtAnimator(this.getContext(), .5);
  this._panZoomCanvas.zoomTo(newScale, undefined, undefined, animator);
  animator.play();
}

DvtZoomSlider.prototype.HandleZoomSliderCrosslineRollOver = function (event) {
  var i;
  var numCrosslines = 0;
  if (this._zoomSliderCrosslines != null)
    numCrosslines = this._zoomSliderCrosslines.length;
  for (i = 0;i < numCrosslines;i++) {
    //find the crossline that was rolled over, so we know the index,
    //which is the same as the index of its tooltip in the array
    if (event.target == this._zoomSliderCrosslines[i])
      break;
  }

  var tooltip = null;
  if (i < this._zoomLevelsTooltips.length)
    tooltip = this._zoomLevelsTooltips[i];

  if (tooltip && this.getTooltipManager()) {
    this.getTooltipManager().showTooltip(tooltip, event.target);
  }
}

DvtZoomSlider.prototype.HandleZoomSliderCrosslineRollOut = function (event) {
  if (this.getTooltipManager() != null)
    this.getTooltipManager().hideTooltip();
}

/**
 *this.Handle a mouse down on the zoom slider thumb.
 *
 * @param event MouseEvent
 */
DvtZoomSlider.prototype.HandleZoomSliderThumbMouseDown = function (event) {
  //don't want click to fall through to other components
  DvtEventManager.consumeEvent(event);

  this._bZoomSliderThumbMouseDown = true;
  //save the offset of the mouse position relative to the thumb position
  //this._zoomSliderThumbMouseDeltaY = event.localY;
  //BUG FIX #7241349: listen to mouse move event on thumb so we can 
  //make the drag animation smooth
  //BUG FIX #10079178: add mouseUp listener on the stage so that
  //we can detect it even if the mouse is outside of the thumb
  stage.addEventListener("mousemove", this.HandleZoomSliderThumbMouseMove, false, 0, true);
  stage.addEventListener("mouseup", this.HandleZoomSliderThumbMouseUp, false, 0, true);

  var dragBounds = new Rectangle(this._zoomSliderThumb.x, this._zoomSliderStartY - this._zoomSliderThumb.height / 2, 0, this._zoomSliderEndY - this._zoomSliderStartY);
  this._zoomSliderThumb.startDrag(false, dragBounds);
}

/**
 *this.Handle a mouse move on the zoom slider thumb.
 *
 * @param event MouseEvent
 */
DvtZoomSlider.prototype.HandleZoomSliderThumbMouseMove = function (event) {
  //only respond if the mouse down occurred on the thumb
  if (this._bZoomSliderThumbMouseDown) {
    //BUG FIX #7241349: update the view while dragging so that the
    //animation is smooth
    event.updateAfterEvent();

    /*
        //don't want click to fall through to other components
        DvtEventManager.consumeEvent(event);
        
        //calculate new thumb position
        var newY = this._zoomSliderThumb.y + event.localY - this._zoomSliderThumbMouseDeltaY;
        //constrain thumb position to length of zoom slider line
        if (newY < this._zoomSliderStartY - this._zoomSliderThumb.height / 2)
          newY = this._zoomSliderStartY - this._zoomSliderThumb.height / 2;
        else if (newY > this._zoomSliderEndY - this._zoomSliderThumb.height / 2)
          newY = this._zoomSliderEndY - this._zoomSliderThumb.height / 2;
        //set the new thumb position
        this._zoomSliderThumb.y = newY;
        */
  }
}

/**
 *this.Handle a mouse up on the zoom slider thumb.
 *
 * @param event MouseEvent
 */
DvtZoomSlider.prototype.HandleZoomSliderThumbMouseUp = function (event) {
  //BUG FIX #7241349: remove mouse move listener on thumb
  //BUG FIX #10079178: remove mouseUp listener that was added when we got the mouseDown event
  stage.removeEventListener("mousemove", this.HandleZoomSliderThumbMouseMove);
  stage.removeEventListener("mouseup", this.HandleZoomSliderThumbMouseUp);

  if (this._bZoomSliderThumbMouseDown) {
    //don't want click to fall through to other components
    DvtEventManager.consumeEvent(event);

    this._bZoomSliderThumbMouseDown = false;

    this._zoomSliderThumb.stopDrag();

    //calculate the new zoom ratio
    var relY = this._zoomSliderEndY - (this._zoomSliderThumb.y + this._zoomSliderThumb.height / 2);
    if (relY < 0)
      relY = 0;
    var ratio = this._minScale + (relY / (this._zoomSliderEndY - this._zoomSliderStartY)) * (this._maxScale - this._minScale);

    var animator = new DvtAnimator(this.getContext(), .5);
    this._panZoomCanvas.zoomTo(ratio, undefined, undefined, animator);
    animator.play();
  }
}



/**
 * PanControl for use with Diagram.
 */
var DvtPanControl = function (context, panButton, recenterButton, panZoomCanvas, resources, control) {
  this.Init(context, panButton, recenterButton, panZoomCanvas, resources, control);
};

DvtPanControl.PAN_TIMER_INTERVAL = 50; //in ms
DvtPanControl.PAN_TIMER_ACCELERATE_COUNT = 2 * //seconds
                                          (1000 / DvtPanControl.PAN_TIMER_INTERVAL);//iterations per second
                                           
/*
 * make DvtPanControl a subclass of DvtBasePanZoomControl
 */
DvtObj.createSubclass(DvtPanControl, DvtBasePanZoomControl, "DvtPanControl");

/*--------------------------------------------------------------------*/
/*  Init()                                                            */
/*--------------------------------------------------------------------*/
DvtPanControl.prototype.Init = function (context, panButton, recenterButton, panZoomCanvas, resources, control) {
  DvtPanControl.superclass.Init.call(this, context, panZoomCanvas, resources);
  
  this._panTimer = new DvtTimer(context, DvtPanControl.PAN_TIMER_INTERVAL, 
                                this.HandlePanTimer, this);
  this._bPanMouseDown = false;
  this._panCenter = new DvtPoint(20, 20);

  if (control) {
    this._controls = control;
  }
  else {
    this._controls = DvtPanZoomControlPanel.CONTROLS_ALL;
  }
  
  panButton.addChild(recenterButton);
  if (recenterButton) {
    this._panCenterSprite = recenterButton;
    this._panCenterSprite.setVisible(false);
    this._panCenterSprite.addEventListener("click", this.HandlePanCenterClick, false, this);
    this._panCenterSprite.addEventListener("mousedown", this.HandlePanCenterDown, false, this);
    this._panCenterSprite.addEventListener("mouseup", this.HandlePanCenterUp, false, this);
    this._panCenterSprite.addEventListener("mouseover", this.HandlePanCenterRollOver, false, this);
    this._panCenterSprite.addEventListener("mouseout", this.HandlePanCenterRollOut, false, this);

    //Bug 13436842 - pan control rotates when hovering over the re-center button
    this._panCenterSprite.addEventListener("mousemove", this.HandlePanCenterMouseMove, false, this);
  }
  
  this._panButton = panButton;
  this._panButton.addEventListener("click", this.HandlePanClick, false, this);
  this._panButton.addEventListener("mousedown", this.HandlePanMouseDown, false, this);
  this._panButton.addEventListener("mouseup", this.HandlePanMouseUp, false, this);
  this._panButton.addEventListener("mouseover", this.HandlePanRollOver, false, this);
  this._panButton.addEventListener("mouseout", this.HandlePanRollOut, false, this);
  this._panButton.addEventListener("mousemove", this.HandlePanMouseMove, false, this);
  
  this._panControl = DvtButtonLAFUtils.createPanButtonBackground(context);
  this._panControl.addChild(this._panButton);
  this.addChild(this._panControl);
}

/**
 * Handle a click on the pan control.
 *
 * @param event MouseEvent
 */
DvtPanControl.prototype.HandlePanClick = function (event) {
  //don't want click to fall through to other components
  DvtEventManager.consumeEvent(event);
  // this._logEvent(event);
}

/**
 * Handle a mouse down on the pan control.
 *
 * @param event MouseEvent
 */
DvtPanControl.prototype.HandlePanMouseDown = function (event) {

  //don't want click to fall through to other components
  DvtEventManager.consumeEvent(event);
  // this._logEvent(event);

  this._bPanMouseDown = true;
//  this._panButton._mouseDownHandler(event);

  //hide the pan center button
  if (this._panCenterSprite)
    this._panCenterSprite.setVisible (false);

  //hide the static compass arrows and show the dynamic directional arrow
  //this._panCompassArrows.visible = false;
  //this._panDirectionalArrow.visible = true;
  //rotate the directional arrow to point towards the mouse
  var stagePoint = this._panZoomCanvas.GetRelativeMousePosition(event);
  var localPoint = this._panButton.stageToLocal(stagePoint);
  this._rotatePanControlDirectionalArrow(localPoint.x, localPoint.y, this._panButton.downState);

  //save the mouse point
  this._panMousePoint = new DvtPoint(localPoint.x, localPoint.y);

  //start the pan timer for continuous panning while the mouse button
  //is held down
  this._panTimer.start();
}

/**
 * Handle a mouse up on the pan control.
 *
 * @param event MouseEvent
 */
DvtPanControl.prototype.HandlePanMouseUp = function (event) {

  if (this._bPanMouseDown) {
    //don't want click to fall through to other components
    DvtEventManager.consumeEvent(event);
    this._bPanMouseDown = false;

    //show the pan center button
    if (this._panCenterSprite) {
      this._panCenterSprite.setVisible(true);
    }

    //show the static compass arrows and hide the dynamic directional arrow
    //this._panCompassArrows.visible = true;
    //this._panDirectionalArrow.visible = false;
    //stop the pan timer and reset it for use next time
    this._panTimer.stop();
//     this._panTimer.reset();

  }

  // this._logEvent(event);
  this._panButton._mouseOutHandler(event);

  this.HideTooltip();
}

/**
 * Handle a mouse roll over on the pan control.
 *
 * @param event MouseEvent
 */
DvtPanControl.prototype.HandlePanRollOver = function (event) {
//  DvtEventManager.consumeEvent(event);
  // this._logEvent(event);

  //show the pan center button
  if (this._panCenterSprite) {
    this._panCenterSprite.setVisible(true);
  }

//  this._panButton._mouseOverHandler(event);
    this.ShowTooltip(event, this.getResources().getPanControlTooltip());
}

/**
 * Handle a mouse roll out on the pan control.
 *
 * @param event MouseEvent
 */
DvtPanControl.prototype.HandlePanRollOut = function (event) {
  this.HandlePanMouseUp(event);

  //hide the pan center button
  if (this._panCenterSprite) {
    this._panCenterSprite.setVisible(false);
  }
}

/**
 * Handle a mouse move on the pan control.
 *
 * @param event MouseEvent
 */
DvtPanControl.prototype.HandlePanMouseMove = function (event) {

  //don't want click to fall through to other components
  DvtEventManager.consumeEvent(event);
  // this._logEvent(event);

  var stagePoint = this._panZoomCanvas.GetRelativeMousePosition(event);
  var localPoint = this._panButton.stageToLocal(stagePoint);
  if (this._bPanMouseDown) {
    //rotate the directional arrow to point towards the mouse
    this._rotatePanControlDirectionalArrow(localPoint.x, localPoint.y, this._panButton.downState);

    //save the mouse point
    this._panMousePoint = new DvtPoint(localPoint.x, localPoint.y);

//     event.updateAfterEvent();
  }
  //BUG FIX #7352517: rotate the arrows shown when the mouse is over
  //the pan control
  else {
    //rotate the directional arrow to point towards the mouse
    this._rotatePanControlDirectionalArrow(localPoint.x, localPoint.y, this._panButton.overState);
  }
}

/**
 * Handle a pan timer event to pan the view.
 *
 * @param event TimerEvent
 */
DvtPanControl.prototype.HandlePanTimer = function () {

  if (this._bPanMouseDown) {
    //TODO: ???
//     event.updateAfterEvent();

    //get the difference between the mouse position and the center of the pan control
    var diffY = this._panCenter.y - this._panMousePoint.y;
    var diffX = this._panCenter.x - this._panMousePoint.x;

    //calculate the slope and angle of the mouse from the center of the pan control
    //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;
    this._panZoomCanvas.panBy(factor * deltaX, factor * deltaY);
  }
}

/**
 * Handle a mouse click on the center of the pan control.
 *
 * @param event MouseEvent
 */
DvtPanControl.prototype.HandlePanCenterClick = function (event) {

  //don't want click to fall through to other components
  DvtEventManager.consumeEvent(event);
  this._logEvent(event);
  this._panZoomCanvas.zoomAndCenter();
}

/**
 * Handle a mouse down on the center of the pan control.
 *
 * @param event MouseEvent
 */
DvtPanControl.prototype.HandlePanCenterDown = function (event) {

  //don't want down to fall through to other components
  DvtEventManager.consumeEvent(event);
  this._logEvent(event);

//  this._panCenterSprite._mouseDownHandler(event);
}

/**
 * Handle a mouse up on the center of the pan control.
 *
 * @param event MouseEvent
 */
DvtPanControl.prototype.HandlePanCenterUp = function (event) {

  //don't want up to fall through to other components
  DvtEventManager.consumeEvent(event);
  this._logEvent(event);

//  this._panCenterSprite._mouseOutHandler(event);
  this.HideTooltip();
}

/**
 * Handle a mouse roll over on the center of the pan control.
 *
 * @param event MouseEvent
 */
DvtPanControl.prototype.HandlePanCenterRollOver = function (event) {
  //don't want up to fall through to other components
  // DvtEventManager.consumeEvent(event);
  this._logEvent(event);

//  this._panCenterSprite._mouseOverHandler(event);
  this.ShowTooltip(event, this.getResources().getPanControlCenterTooltip());

  //Bug 13399111 - Wrong tooltip for pan control center button
  //Bug 13436842 - pan control rotates when hovering over the re-center button
  this._panCenterSprite.setVisible(true);
  DvtEventManager.consumeEvent(event);
};

/**
 * Handle a mouse roll out on the center of the pan control.
 *
 * @param event MouseEvent
 */
DvtPanControl.prototype.HandlePanCenterRollOut = function (event) {
  //don't want up to fall through to other components
//  DvtEventManager.consumeEvent(event);
  this._logEvent(event);

//  this._panCenterSprite._mouseOutHandler(event);
  this.HideTooltip();

  //Bug 13399111 - Wrong tooltip for pan control center button
  //Bug 13436842 - pan control rotates when hovering over the re-center button
  DvtEventManager.consumeEvent(event);
}

/**
 * Handle a mouse move on the center of the pan control.
 *
 * @param event MouseEvent
 */
DvtPanControl.prototype.HandlePanCenterMouseMove = function (event) {
  //Bug 13436842 - pan control rotates when hovering over the re-center button
  DvtEventManager.consumeEvent(event);
  this._logEvent(event);
}

/**
 * Rotate the pan control directional arrow to point towards the mouse.
 *
 * @param localX local mouse X coord
 * @param localY local mouse Y coord
 * @param displayObj DisplayObject to rotate
 */
DvtPanControl.prototype._rotatePanControlDirectionalArrow = function (localX, localY, displayObj) {
  var xOffset = localX - this._panCenter.x;
  var yOffset = localY - this._panCenter.y;

  // var angleDeg = Math.atan2(yOffset, xOffset) * RAD_TO_DEG;
  var angleRad = Math.atan2(yOffset, xOffset);

  //adjust the angle by 45 degrees because the image has the arrow pointing 
  //down to the right at an angle of 45 degrees
  // displayObj.rotation = angleDeg - 45;
  displayObj.setRotation(angleRad - Math.PI/4);

}


/**
 * DvtPanZoomControlPanel for use with PanZoomCanvas.
 * This panel contains tools for zooming and panning.
 * By default, the panel is initially collapsed.
 */
var DvtPanZoomControlPanel = function(context, canvas, buttonImages, resources, controls) {
  this.Init(context, canvas, buttonImages, resources, controls);
};

/*
 * make DvtPanZoomControlPanel a subclass of DvtContainer
 */
DvtObj.createSubclass(DvtPanZoomControlPanel, DvtContainer, "DvtPanZoomControlPanel");

DvtPanZoomControlPanel.STATE_COLLAPSED = 0;
DvtPanZoomControlPanel.STATE_EXPANDED = 1;
DvtPanZoomControlPanel.CONTROLS_ALL = 0xffffff;
DvtPanZoomControlPanel.CONTROLS_ZOOM_SLIDER   = 0x000001;
DvtPanZoomControlPanel.CONTROLS_CENTER_BUTTON = 0x000010;
DvtPanZoomControlPanel.CONTROLS_ZOOM_TO_FIT_BUTTON = 0x000100;
DvtPanZoomControlPanel.CONTROLS_ZOOM = 0x001001; // include CONTROLS_ZOOM_SLIDER
DvtPanZoomControlPanel.EXPAND_EVENT = "dvtPZCPExpand";
DvtPanZoomControlPanel.COLLAPSE_EVENT = "dvtPZCPCollapse";

DvtPanZoomControlPanel.ALPHA_ROLLOUT_BACKGROUND = .2;
DvtPanZoomControlPanel.ALPHA_ROLLOUT_FRAME = 1;
DvtPanZoomControlPanel.ALPHA_ROLLOVER_BACKGROUND = .6;
DvtPanZoomControlPanel.ALPHA_ROLLOVER_FRAME = 1;
DvtPanZoomControlPanel.ALPHA_MOUSE_DRAG_PANNING_BACKGROUND = .2;
DvtPanZoomControlPanel.ALPHA_MOUSE_DRAG_PANNING_FRAME = .3;

/*--------------------------------------------------------------------*/
/*  Init()                                                            */
/*--------------------------------------------------------------------*/
/**
 * Constructor.
 * 
 * @param canvas PanZoomCanvas this control panel will be associated with
 * @param tooltipManageranager TooltipManager this control panel should use
 * @param resources translated resource strings
 * @param controls bit mask specifying which controls to show; constants are 
 *        defined in this class and begin with CONTROLS_; the default
 *        is CONTROLS_ALL
 */
DvtPanZoomControlPanel.prototype.Init = function(context, canvas, buttonImages, resources, controls) {

  DvtPanZoomControlPanel.superclass.Init.call(this, context, "_controlPanel");
  
  this._bMouseDragPanning = false;
  this._bMouseOver = false;

  this._panZoomCanvas = canvas;
  this._tooltipManager = context.getTooltipManager();
  this._resources = resources;
  if (! controls) {
    controls = DvtPanZoomControlPanel.CONTROLS_ALL;
  }
  this._controls = controls;

  this._zoomLevels = DvtZoomSlider.DEFAULT_ZOOM_LEVELS;
  this._zoomSliderMinScale = 0;
  this._zoomSliderMaxScale = 1;
  this._state = DvtPanZoomControlPanel.STATE_COLLAPSED;
  
  this._buttonImages = buttonImages;
      
  var s = this.RenderCollapsed();
  this._background.setAlpha(DvtPanZoomControlPanel.ALPHA_ROLLOUT_BACKGROUND);
  this._frame.setAlpha(DvtPanZoomControlPanel.ALPHA_ROLLOUT_FRAME);
  this.addChild(s);

  this._addMouseRollOverListeners();
  this.addEventListener("mousedown", DvtEventManager.consumeEvent, false, this);
  this.addEventListener("click", DvtEventManager.consumeEvent, false, this);

  //TODO??
  //DvtButtonLAFUtils.dropShadowFilter(this);
      
  //TOOD: disable tabbing for control panel tools
  this.tabChildren = false;

};

/**
 * Set the state of this control panel.
 * 
 * @param state STATE_EXPANDED or STATE_COLLAPSED
 */
DvtPanZoomControlPanel.prototype.setState = function(state) {
  if (this._state != state) {
    this.toggleExpandCollapse();
    this._state = state;
  }
};

/**
 * Get the state of this control panel.
 * 
 * @return STATE_EXPANDED or STATE_COLLAPSED
 */
DvtPanZoomControlPanel.prototype.getState = function() {
  return this._state;
};

/**
 * Set the discrete zoom levels that appear in the zoom slider.
 * 
 * @param zoomLevels array of discrete zoom levels to show in the zoom slider
 */
DvtPanZoomControlPanel.prototype.setZoomLevels = function(zoomLevels) {
  if (! zoomLevels) {
      zoomLevels = ZoomSlider.DEFAULT_ZOOM_LEVELS;
  }
  this._zoomLevels = zoomLevels;
  this.replaceZoomSlider();
};

/**
 * Get the discrete zoom levels that appear in the zoom slider.
 * 
 * @return array of discrete zoom levels to show in the zoom slider
 */
DvtPanZoomControlPanel.prototype.getZoomLevels = function() {
  return this._zoomLevels;
};
    
/**
 * Set the tooltips for the discrete zoom levels in the zoom slider.
 * 
 * @param zoomLevelsTooltips tooltips for discrete zoom levels in zoom slider
 */
DvtPanZoomControlPanel.prototype.setZoomLevelsTooltips = function(zoomLevelsTooltips) {
  this._zoomLevelsTooltips = zoomLevelsTooltips;
  if (this._zoomSlider) {
     this._zoomSlider.setZoomLevelsTooltips(zoomLevelsTooltips);
  }
};
    
/**
 * Get the tooltips for the discrete zoom levels in the zoom slider.
 * 
 * @return tooltips for discrete zoom levels in zoom slider
 */
DvtPanZoomControlPanel.prototype.getZoomLevelsTooltips = function() {
  return this._zoomLevelsTooltips;
};
    
/**
 * Set the tooltip manager to use.
 * 
 * @param tooltipManager manager to use
 */
DvtPanZoomControlPanel.prototype.setTooltipManager = function(tooltipManager) {
  this._tooltipManager = tooltipManager;
  if (this._zoomSlider) {
    this._zoomSlider.setTooltipManager(tooltipManager);
  }
  if (this._zoomInButton)
    this._zoomInButton.setTooltipManager(tooltipManager);
  if (this._zoomOutButton)
    this._zoomOutButton.setTooltipManager(tooltipManager);
  if (this._zoomToFitButton)
    this._zoomToFitButton.setTooltipManager(tooltipManager);

};

/**
 * Get the tooltip manager being used.
 * 
 * @return tooltipManager being used
 */
DvtPanZoomControlPanel.prototype.getTooltipManager = function() {
  return this._tooltipManager;
};

/**
 * Set the translated resources to use.
 * 
 * @param resources translated resources
 */
DvtPanZoomControlPanel.prototype.setResources = function(resources) {
  this._resources = resources;
      
  if (this._zoomSlider)
    this._zoomSlider.setResources(resources);
  if (this._zoomInButton)
    this._zoomInButton.setResources(resources);
  if (this._zoomOutButton)
    this._zoomOutButton.setResources(resources);
  if (this._zoomToFitButton)
    this._zoomToFitButton.setResources(resources);
};

/**
 * Get the translated resources to use.
 * 
 * @return translated resources
 */
DvtPanZoomControlPanel.prototype.getResources = function() {
  if (this._resources == null)
    this._resources = new DvtPanZoomControlPanelResources();
  return this._resources;
};

/**
 * Set the min scale for the zoom slider.
 * 
 * @param minScale minimum value of zoom slider
 */
DvtPanZoomControlPanel.prototype.setZoomSliderMinScale = function(minScale) {
  this._zoomSliderMinScale = minScale;
  this.replaceZoomSlider();
}

/**
 * Get the min scale for the zoom slider.  
 * 
 * @return minimum value of zoom slider
 */
DvtPanZoomControlPanel.prototype.getZoomSliderMinScale = function() {
  return this._zoomSliderMinScale;
}
    
/**
 * Set the max scale for the zoom slider.
 * 
 * @param maxScale maximum value of zoom slider
 */
DvtPanZoomControlPanel.prototype.setZoomSliderMaxScale = function(maxScale) {
  this._zoomSliderMaxScale = maxScale;
  this.replaceZoomSlider();
}

/**
 * Get the max scale for the zoom slider.  
 * 
 * @return maximum value of zoom slider
 */
DvtPanZoomControlPanel.prototype.getZoomSliderMaxScale = function() {
  return this._zoomSliderMaxScale;
}

/**
 * Notify the control panel that mouse drag panning has started.
 */
DvtPanZoomControlPanel.prototype.mouseDragPanningStarted = function() {
  this._bMouseDragPanning = true;
  //disable interaction
  this._background.setMouseEnabled(false);
  this._frame.setMouseEnabled(false);
  
  //change alphas of background and frame
  this._background.setAlpha(DvtPanZoomControlPanel.ALPHA_MOUSE_DRAG_PANNING_BACKGROUND);
  this._frame.setAlpha(DvtPanZoomControlPanel.ALPHA_MOUSE_DRAG_PANNING_FRAME);
};

/**
 * Notify the control panel that mouse drag panning has ended. 
 */
DvtPanZoomControlPanel.prototype.mouseDragPanningEnded = function() {
  this._bMouseDragPanning = false;
  //enable interaction
  this._background.setMouseEnabled(true);
  this._frame.setMouseEnabled(true);
    
  //change alphas based on whether the mouse is currently over
  //the control panel
  var bMouseOver = this._bMouseOver;//TODO???hitTestPoint(stage.mouseX, stage.mouseY, true);
  if (bMouseOver) {
    this.HandleRollOver(null);
  }
  else {
    this.HandleRollOut(null);
  }
};

/**
 * Rotate a given DisplayObject to pseudo-flip it horizontally for BiDi.
 * Vertically the DisplayObject must be the exact image of itself because it will
 * end up upside down.  
 * 
 * @param dispObj DisplayObject to rotate
 */
DvtPanZoomControlPanel.prototype.rotateControlPanelDisplayObject = function(dispObj) {
  dispObj.setRotation(Math.PI);

  //TODO: real getDimensions???
//   var dim = DvtButtonLAFUtils._getDimForced(context, dispObj);
  var dim = dispObj.getDimensions();
  dispObj.setTranslateX(dispObj.getTranslateX() + dim.w);
  dispObj.setTranslateY(dispObj.getTranslateY() + dim.h);
};

//BUG FIX #10154856: show single row of controls in TMap
/**
 * Determine whether the horizontal arm of the control panel shows a single row or
 * two rows.
 * The default is false, meaning that the control panel shows two rows.
 * 
 * @return true if the horizontal arm of the control panel shows a single row
 */
DvtPanZoomControlPanel.prototype.isSingleHorzRow = function() {
  return true;
};
    
//BUG FIX #10154856: show single row of controls in TMap
/**
 * Get the height of the horizontal arm of the control panel.
 * 
 * @return height of the horizontal arm of the control panel
 */
DvtPanZoomControlPanel.prototype.getViewPanelHeight = function() {
  if (this.isSingleHorzRow())
    return DvtPanZoomControlPanelLAFUtils.getViewPanelHalfHeight();
  else
    return DvtPanZoomControlPanelLAFUtils.getViewPanelHeight();
};

/**
 * Render the collapsed state of the panel.
 * 
 * @return Sprite representing collapsed state
 */
DvtPanZoomControlPanel.prototype.RenderCollapsed = function() {
  var context = this.getContext();

  var contentBar = new DvtContainer(context);
  var hh = this.getViewPanelHeight();

  this._background = DvtPanZoomControlPanelLAFUtils.createEmptyViewClosedShape(context, hh);
  this._frame = DvtPanZoomControlPanelLAFUtils.createEmptyViewClosedFrame(context, hh);

  //Note: get dimensions before adding expand button
  this._collapsedDim = DvtButtonLAFUtils._getDimForced(context, this._frame);

  //create the control panel expand button
  this._expandButton = DvtButtonLAFUtils.createExpandButton(this.getContext(), this._buttonImages, this.getViewPanelHeight());
  this._expandButton.addEventListener("click", this.HandleExpandClick, false, this);
  this._expandButton.addEventListener("mousedown", DvtEventManager.consumeEvent, false, this);
  this._expandButton.addEventListener("mouseover", this.HandleExpandRollOver, false, this);
  this._expandButton.addEventListener("mouseout", this.HandleExpandRollOut, false, this);
  this._frame.addChild(this._expandButton);

  //BiDi: flip the frame and background
  if (DvtStyleUtils.isLocaleR2L()) {
    this.rotateControlPanelDisplayObject(this._background);
    this.rotateControlPanelDisplayObject(this._frame);
  }

  contentBar.addChild(this._background);
  contentBar.addChild(this._frame);

  return contentBar;
};

/**
 * Render the expanded state of the panel.
 * 
 * @return Sprite representing expanded state
 */
DvtPanZoomControlPanel.prototype.RenderExpanded = function() {
  var context = this.getContext();
  var s = new DvtContainer(context);
  var dim;

  this._background = new DvtContainer(context);
  this._frame = new DvtContainer(context);
  s.addChild(this._background);
  s.addChild(this._frame);

  //BiDi
  var bBiDi = DvtStyleUtils.isLocaleR2L();

  //create the horizontal content bar, which contains the pan control,
  //layout combo box and control panel collapse button
  var horzContentBar = new DvtContainer(context);
  var nKidHorzContentBar = 0;

  //initialize the starting locations for content bar tools
  var currX = 4;
  var panControl = null;
  var pcDim; 

  //BUG FIX #10154856: underlay to erase intersection between horizontal and 
  //vertical arms of control panel under the pan control
  var panControlUnderlay2 = null;

  //BUG FIX #10154856: determine if the control panel is showing a single or 
  //double row of controls
  var bSingleRow = this.isSingleHorzRow();

  // hide panControl if it is off
  if ((this._controls & DvtPanZoomControlPanel.CONTROLS_CENTER_BUTTON) != 0) {
    this._panControl = DvtButtonLAFUtils.createPanControl(this.getContext(), this._panZoomCanvas, this.getResources(), this._controls, this._buttonImages);

    //BUG FIX #10154856: create underlays to erase the view of the intersection
    //between the horz and vert arms of the control panel under the pan control,
    //and to show a solid background behind the pan control so that it blends
    //in with the rest of the control panel
    if (bSingleRow) {
      this._panControlUnderlay = DvtButtonLAFUtils.createPanButtonUnderlay(context);
      panControlUnderlay2 = DvtButtonLAFUtils.createPanButtonUnderlay(context);

      //need to add this one first, because it will be used to erase everything 
      //underneath
      horzContentBar.addChild(panControlUnderlay2);
      //add this one second, because it will be visible behind the pan control
      horzContentBar.addChild(this._panControlUnderlay);

      //TODO: Note don't have BlendMode in html5
      //need to set container blendMode to LAYER so that we can use ERASE on
      //bottom-most underlay
//       this.blendMode = BlendMode.LAYER;
//       panControlUnderlay2.blendMode = BlendMode.ERASE;
    }

    //offset the pan control slightly
    var xx = 4;
    var yy = 3.5;
    this._panControl.setTranslateX(xx);
    this._panControl.setTranslateY(yy);

    //BUG FIX #10154856: keep underlays in sync with pan control position
    if (this._panControlUnderlay) {
      this._panControlUnderlay.setTranslateX(xx);
      this._panControlUnderlay.setTranslateY(yy);
    }
    if (panControlUnderlay2) {
      panControlUnderlay2.setTranslateX(xx);
      panControlUnderlay2.setTranslateY(yy);
    }

    pcDim = DvtButtonLAFUtils._getDimForced(context, this._panControl);//40, 40
    horzContentBar.addChild(this._panControl);
    nKidHorzContentBar++;

    currX += xx + pcDim.w;
    currX += 1;//49
  }

  //create the control panel collapse button before the layout and panelCard
  //sync buttons so that it will be behind them in the z-order so
  //that their dropdowns will draw over it
  this._collapseButton = DvtButtonLAFUtils.createCollapseButton(this.getContext(), this._buttonImages, this.getViewPanelHeight());
  this._collapseButton.addEventListener("click", this.HandleCollapseClick, false, this);
  this._collapseButton.addEventListener("mousedown", DvtEventManager.consumeEvent, false, this);
  this._collapseButton.addEventListener("mouseover", this.HandleCollapseRollOver, false, this);
  this._collapseButton.addEventListener("mouseout", this.HandleCollapseRollOut, false, this);

  var cbDim = DvtButtonLAFUtils._getDimForced(context, this._collapseButton);//(-6,0,22,26)

  horzContentBar.addChild(this._collapseButton);

  //allow subclasses to put their custom tools in the horzContentBar
  var additionalContent = new DvtContainer(context);
  this.PopulateHorzContentBar(additionalContent);

  // dont render additionalContent if no children
  var acDim;
  if (additionalContent.getNumChildren() > 0) {
    additionalContent.setTranslateX(currX);//50;
    additionalContent.setTranslateY(2);

    acDim = DvtButtonLAFUtils._getDimForced(context, additionalContent);
    horzContentBar.addChild(additionalContent);
    nKidHorzContentBar++;

    currX += acDim.w;
    currX += 3;
  }
  else {
    additionalContent = null;
  }

  //position the control panel collapse button
  this._collapseButton.setTranslateX(currX);
  currX += cbDim.w;

  //save the width of the expanded panel for use during transitioning when expanding
  if (nKidHorzContentBar) {
    this._expandedDim = DvtButtonLAFUtils._getDimForced(context, horzContentBar);
  }
  else {
    this._expandedDim = new DvtRectangle(0, 0, cbDim.w, cbDim.h);
  }

  //BiDi: reverse the order of the controls in the horzContentBar
  if (bBiDi) {
    if (panControl) {
      panControl.setTranslateX(currX - panControl.getTranslateX() - pcDim.w);

      //BUG FIX #10154856: keep underlays in sync with pan control position
      if (this._panControlUnderlay)
        this._panControlUnderlay.setTranslateX(panControl.getTranslateX());

      if (panControlUnderlay2)
        panControlUnderlay2.setTranslateX(panControl.getTranslateX());
    }

    if (additionalContent) {
      additionalContent.setTranslateX(currX - additionalContent.getTranslateX() - acDim.w);
    }

    //TO DO: reverse order of tools in additionalContent
    this._collapseButton.setTranslateX(currX - this._collapseButton.getTranslateX() - cbDim.w);
    this.rotateControlPanelDisplayObject(this._collapseButton);
  }

  //create the vertical content bar, which contains the zoom-to-fit
  //button, zoom in button, zoom out button, and zoom slider
  var vertContentBar = null;

  if ((this._controls & DvtPanZoomControlPanel.CONTROLS_ZOOM_TO_FIT_BUTTON) != 0 ||
      (this._controls & DvtPanZoomControlPanel.CONTROLS_ZOOM) != 0) {

    vertContentBar = new DvtContainer(context);
    this._frame.addChild(vertContentBar);

    vertContentBar.setTranslateY(horzContentBar.getTranslateY() + (this.getViewPanelHeight() + 1));//27
  }

  //Bug 13376596 - pan control in control panel difficult to use
  //problem is part of the vertical content bar overlap the pan control
  this._frame.addChild(horzContentBar);

  //render background after all tools have been added so that
  //we know the size of the content bar
  //render the view frame and shape based on the required width of the panel 
  if (additionalContent || 
      (this._controls & DvtPanZoomControlPanel.CONTROLS_CENTER_BUTTON) > 0) {
    var viewFrame = 
      DvtPanZoomControlPanelLAFUtils.createEmptyViewOpenFrame(context, currX, 
                                                              this.getViewPanelHeight(), ! bBiDi);
    horzContentBar.addChildAt(viewFrame, 0);
  }

  var viewShape = null;
  if (nKidHorzContentBar) {
    viewShape = DvtPanZoomControlPanelLAFUtils.createEmptyViewOpenShape(context, currX, 
                                                              this.getViewPanelHeight(), ! bBiDi);
  }

  var currY = 2;

  //BUG FIX #10154856: if showing a single horizontal row, offset the start of the 
  //vertical controls to leave room for the pan control
  if (bSingleRow)
    currY += (DvtPanZoomControlPanelLAFUtils.getViewPanelHeight() - this.getViewPanelHeight());

  //create the zoom to fit button
  if ((this._controls & DvtPanZoomControlPanel.CONTROLS_ZOOM_TO_FIT_BUTTON) != 0) {
    this._zoomToFitButton = DvtButtonLAFUtils.createZoomToFitButton(this.getContext(), this._panZoomCanvas, this.getResources(), this._buttonImages);
    this._zoomToFitButton.setTranslateX(2.5);
    this._zoomToFitButton.setTranslateY(this._zoomToFitButton.getTranslateY() + currY);

    dim = DvtButtonLAFUtils._getDimForced(context, this._zoomToFitButton);
    vertContentBar.addChild(this._zoomToFitButton);
    currY += dim.h + 2;
  }

  //create the zoom in button
  if ((this._controls & DvtPanZoomControlPanel.CONTROLS_ZOOM) != 0) {
    this._zoomInButton = DvtButtonLAFUtils.createZoomInButton(this.getContext(), this._panZoomCanvas, this.getResources(), this._buttonImages);
    this._zoomInButton.setTranslateX(2.5);
    this._zoomInButton.setTranslateY(this._zoomInButton.getTranslateY() + currY);

    dim = DvtButtonLAFUtils._getDimForced(context, this._zoomInButton);
    vertContentBar.addChild(this._zoomInButton);
    currY += dim.h + 2;

    if (this.isZoomSliderShown()) {
      //create the zoom slider
      this._zoomSlider = this.createZoomSlider();
      this._zoomSlider.setTranslateX(2.5);
      this._zoomSlider.setTranslateY(this._zoomSlider.getTranslateY() + currY);

      dim = DvtButtonLAFUtils._getDimForced(context, this._zoomSlider);
      vertContentBar.addChild(this._zoomSlider);
      currY += dim.h + 2;
    }

    //create the zoom out button
    this._zoomOutButton = DvtButtonLAFUtils.createZoomOutButton(this.getContext(), this._panZoomCanvas, this.getResources(), this._buttonImages);
    this._zoomOutButton.setTranslateX(2.5);
    this._zoomOutButton.setTranslateY(this._zoomOutButton.getTranslateY() + currY);

    dim = DvtButtonLAFUtils._getDimForced(context, this._zoomOutButton);
    vertContentBar.addChild(this._zoomOutButton);
    currY += dim.h;
  }


  //render background after all tools have been added so that
  //we know the size of the content bar
      
  var zoomShape;
  var zoomFrame;
  if (vertContentBar) {
    // Bug 9686175 - controlPanel looks bad when featuresOff="pan zoom cardSync changeLayout"
    // if zoom-to-fit is the only button left on vertContentBar and there is no buttonss in
    // horzContentBar, make bottom right corner square (not round)

    // Bug 10080038 - inconsistent control panel changes for different values of featuresoff
    // featuresOff = "pan zoom"
    var nKidsVert = vertContentBar.getNumChildren();
    var roundedCorner =
      (nKidsVert > 1 || this._zoomToFitButton == null || nKidHorzContentBar > 1 ||
       (nKidHorzContentBar > 0 && nKidsVert == 1));

    dim = DvtButtonLAFUtils._getDimForced(context, horzContentBar);

    currY += 4;
    var cpHeight = roundedCorner? currY: Math.max(dim.h, currY); //84
    zoomShape = DvtPanZoomControlPanelLAFUtils.renderEmptyZoomShape(context, cpHeight);
    zoomFrame = DvtPanZoomControlPanelLAFUtils.renderEmptyZoomFrame(context, cpHeight, roundedCorner);
  }

  //BiDi: align the vertContentBar to the right side of the control panel
  if (! additionalContent &&
      (this._controls & DvtPanZoomControlPanel.CONTROLS_CENTER_BUTTON) == 0) {

    if (bBiDi) {
      horzContentBar.setTranslateX(this._expandedDim.w - horzContentBar.getTranslateX() - 
                                   horzContentBar.getDimensions().w - 1);
      horzContentBar.setTranslateY(0);

      if (vertContentBar) {
        dim = DvtButtonLAFUtils._getDimForced(context, vertContentBar);
        horzContentBar.setTranslateX(horzContentBar.getTranslateX() - vertContentBar.getTranslateX() - dim.w);
        vertContentBar.setTranslateX(this._expandedDim.w - vertContentBar.getTranslateX() - dim.w + 1);
        vertContentBar.setTranslateY(0);
      }
    }
    else {
      if (vertContentBar) {
        dim = DvtButtonLAFUtils._getDimForced(context, vertContentBar);

        vertContentBar.setTranslateX(0);
        vertContentBar.setTranslateY(0);
        horzContentBar.setTranslateX(vertContentBar.getTranslateX() + dim.w - 3);
      }
      else {
        horzContentBar.setTranslateX(horzContentBar.getTranslateX() - 3);
      }
      horzContentBar.setTranslateY(0);
    }
  }
  else if (bBiDi && vertContentBar) {
    dim = DvtButtonLAFUtils._getDimForced(context, vertContentBar);
    vertContentBar.setTranslateX(this._expandedDim.w - vertContentBar.getTranslateX() - dim.w + 1);
  }

  if (viewShape) {
    this._background.addChild(viewShape);

    viewShape.setTranslateX(horzContentBar.getTranslateX());
    viewShape.setTranslateY(horzContentBar.getTranslateY());
  }

  if (vertContentBar) {
    this._background.addChild(zoomShape);

    vertContentBar.addChildAt(zoomFrame, 0);

    zoomShape.setTranslateX(vertContentBar.getTranslateX());
    zoomShape.setTranslateY(vertContentBar.getTranslateY());
  }

  return s;
};

/**
 * Populate the horzContentBar with additional tools.  
 * This implementation does nothing.  Subclasses should override to include
 * custom tools in the control panel.  
 * 
 * @param contentSprite sprite to contain additional tools that will be
 *        added to the horzContentBar
 */
DvtPanZoomControlPanel.prototype.PopulateHorzContentBar = function(contentSprite) {
  //do nothing
};

/**
 * Toggle the state of the control panel between expanded and collapsed.
 */
DvtPanZoomControlPanel.prototype.toggleExpandCollapse = function() {
  if (this._state === DvtPanZoomControlPanel.STATE_EXPANDED) {
    this._doCollapse();
  }
  else {
    this._doExpand();
  }
};

/**
 * Get the dimensions of the panel in its collapsed state.
 * 
 * @return dimensions of collapsed panel
 */
DvtPanZoomControlPanel.prototype.getCollapsedDim = function() {
  return this._collapsedDim;
};

DvtPanZoomControlPanel.prototype.getExpandedDim = function() {
  return this._expandedDim;
};

/**
 *this.Handle a mouse click on the expand button.
 * 
 * @param event MouseEvent
 */
DvtPanZoomControlPanel.prototype.HandleExpandClick = function(event) {
  //don't want click to fall through to other components
  DvtEventManager.consumeEvent(event);
      
  this._doExpand();
};

/**
 * Expand the control panel.
 */
DvtPanZoomControlPanel.prototype._doExpand = function() {
  //render the expanded control panel
  var s = this.RenderExpanded();
  this.addChild(s);

  //apply alphas based on mouse position
  this._applyAlphasForMouse();

  //transition from the collapsed control panel to the expanded one
  this.transitionExpand(this.getChildAt(0), s);

  this._state = DvtPanZoomControlPanel.STATE_EXPANDED;
      
  //BUG FIX #8719343: fire expand event
  this.fireExpandEvent();
};

/**
 * Apply alphas based on whether the mouse is over the control panel.
 */
DvtPanZoomControlPanel.prototype._applyAlphasForMouse = function() {
  //apply alphas based on whether the mouse is over the control panel
  var bMouseOver = false;
  var stage = this.getContext().getStage();
  if (stage) {
      bMouseOver = true;//TODO???hitTestPoint(stage.mouseX, stage.mouseY, true);
  }
  if (bMouseOver) {
    this._applyAlphasRollover();
  }
  else {
    this._applyAlphasRollout();
  }
}

/**
 *this.Handle a mouse roll over on the expand button.
 * 
 * @param event MouseEvent
 */
DvtPanZoomControlPanel.prototype.HandleExpandRollOver = function(event) {
  if (this.getTooltipManager()) {
    this.getTooltipManager().showTooltip (event.pageX, event.pageY, this.getResources().getControlPanelExpandTooltip(), null, true);
  }
}

/**
 *this.Handle a mouse roll out on the expand button.
 * 
 * @param event MouseEvent
 */
DvtPanZoomControlPanel.prototype.HandleExpandRollOut = function(event) {
  if (this.getTooltipManager()) {
    this.getTooltipManager().hideTooltip();
  }
}

/**
 *this.Handle a mouse click on the collapse button.
 * 
 * @param event MouseEvent
 */
DvtPanZoomControlPanel.prototype.HandleCollapseClick = function(event) {
  //don't want click to fall through to other components
  DvtEventManager.consumeEvent(event);
      
  this._doCollapse();
}

/**
 *this.Handle a mouse click on the collapse button.
 * 
 * @param event MouseEvent
 */
DvtPanZoomControlPanel.prototype._doCollapse = function() {
  //render the collapsed control panel
  var s = this.RenderCollapsed();
  this.addChild(s);

  //apply alphas based on mouse position
  this._applyAlphasForMouse();

  //transition from the expanded control panel to the collapsed one
  this.transitionCollapse(this.getChildAt(0), s);

  this._state = DvtPanZoomControlPanel.STATE_COLLAPSED;

  //BUG FIX #8719343: fire collapse event
  this.fireCollapseEvent();
}

/**
 *this.Handle a mouse roll over on the collapse button.
 * 
 * @param event MouseEvent
 */
DvtPanZoomControlPanel.prototype.HandleCollapseRollOver = function(event) {
  if (this.getTooltipManager()) {
    this.getTooltipManager().showTooltip (event.pageX, event.pageY, this.getResources().getControlPanelCollapseTooltip(), null, true);
  }
}

/**
 *this.Handle a mouse roll out on the collapse button.
 * 
 * @param event MouseEvent
 */
DvtPanZoomControlPanel.prototype.HandleCollapseRollOut = function(event) {
  if (this.getTooltipManager()) {
    this.getTooltipManager().hideTooltip();
  }
}

/**
 * Animate the transition from the collapsed control to the expanded control.
 * 
 * @param oldContent collapsed control
 * @param newContent expanded control
*/ 
DvtPanZoomControlPanel.prototype.transitionExpand = function(oldContent, newContent) {

  //fade out old, fade in new
  var animator = new DvtAnimator(this.getContext(), .25);
  oldContent.setAlpha(1);
  newContent.setAlpha(0);
  animator.addProp(DvtAnimator.TYPE_NUMBER, oldContent, oldContent.getAlpha, oldContent.setAlpha, 0);
  animator.addProp(DvtAnimator.TYPE_NUMBER, newContent, newContent.getAlpha, newContent.setAlpha, 1);

  var odim = oldContent.getDimensions();
  var ndim = newContent.getDimensions();

  //grow new control from size of old
  // newContent.width = oldContent.width;
  // t.$(newContent).scaleX = 1.0;
  newContent.setScaleX(odim.w/ndim.w);
  animator.addProp(DvtAnimator.TYPE_NUMBER, newContent, newContent.getScaleX, newContent.setScaleX, 1);

  //BiDi: need to change x position also, because panel expands out to the left
  if (DvtStyleUtils.isLocaleR2L()) {
    // newContent.x = oldContent.x;
    // t.$(newContent).setTranslateX(oldContent.getTranslateX() + this._collapsedWidth - this._expandedWidth);
    newContent.setTranslateX(oldContent.getTranslateX());
    animator.addProp(DvtAnimator.TYPE_NUMBER, newContent, newContent.getTranslateX, newContent.setTranslateX, 
                     oldContent.getTranslateX() + odim.w - ndim.w);
  }

  // newContent.height = oldContent.height;
  // t.$(newContent).scaleY = 1.0;
  newContent.setScaleY(odim.h/ndim.h);
  animator.addProp(DvtAnimator.TYPE_NUMBER, newContent, newContent.getScaleY, newContent.setScaleY, 1);

  //remove the old control at the end of the transition
  // t.removeChild(oldContent);
  // t.play();

  animator.setOnEnd(function() {oldContent.getParent().removeChild(oldContent)}, this);
  animator.play();
}

/**
 * Animate the transition from the expanded control to the collapsed control.
 * 
 * @param oldContent expanded control
 * @param newContent collapsed control
 */
DvtPanZoomControlPanel.prototype.transitionCollapse = function(oldContent, newContent) {
  var animator = new DvtAnimator(this.getContext(), .25);

  //fade out old, fade in new
  oldContent.setAlpha(1);
  newContent.setAlpha(0);
  animator.addProp(DvtAnimator.TYPE_NUMBER, oldContent, oldContent.getAlpha, oldContent.setAlpha, 0);
  animator.addProp(DvtAnimator.TYPE_NUMBER, newContent, newContent.getAlpha, newContent.setAlpha, 1);

  //shrink old control to size of new
  var ndim = this.getCollapsedDim();
  var odim = this.getExpandedDim();
  animator.addProp(DvtAnimator.TYPE_NUMBER, oldContent, oldContent.getScaleX, oldContent.setScaleX, ndim.w/odim.w);

  //BiDi: need to change x position also, because panel collapses to the right
  if (DvtStyleUtils.isLocaleR2L()) {
    animator.addProp(DvtAnimator.TYPE_NUMBER, oldContent, oldContent.getTranslateX, oldContent.setTranslateX, 
                     newContent.getTranslateX());
  }
  animator.addProp(DvtAnimator.TYPE_NUMBER, oldContent, oldContent.getScaleY, oldContent.setScaleY, ndim.h/odim.h);

  //remove the old control at the end of the transition
  animator.setOnEnd(function() {oldContent.getParent().removeChild(oldContent)}, this);
  animator.play();
}

/**
 * Fire expand event.
 * 
 * @return true if event was dispatched successfully, 
 *         false otherwise
 */
DvtPanZoomControlPanel.prototype.fireExpandEvent = function() {
  var event = new DvtControlPanelEvent(DvtControlPanelEvent.SHOW);
  this.FireListener(event);
  return true;
}

/**
 * Fire collapse event.
 * 
 * @return true if event was dispatched successfully,
 *         false otherwise
 */
DvtPanZoomControlPanel.prototype.fireCollapseEvent = function() {
  var event = new DvtControlPanelEvent(DvtControlPanelEvent.HIDE);
  this.FireListener(event);
  return true;
}

/**
 *this.Handle a layout change event from the view.
 * The UI should update for the new layout.
 * 
 * @param event layout change event
 */
DvtPanZoomControlPanel.prototype.HandleLayoutChangeEvent = function(event) {
  //only need to update if the layout combo box is visible,
  //which is only true when the control panel is expanded
  if (this._state == DvtPanZoomControlPanel.STATE_EXPANDED) {
    //re-render the expanded control panel because that's
    //the easiest way to update, show, or hide the
    //layout combo box
    var s = this.RenderExpanded();
    this.addChild(s);
    this.removeChildAt(0);
  }
}

/**
 * Render the background of the horizontal content bar.
 * 
 * @param s Sprite representing the content bar
 * @param w width of bar
 * @param h height of bar
 */
DvtPanZoomControlPanel.prototype.renderHorzContentBarBackground = function(s, ww, hh) {    
  //gradient effect
  var rot = 0;
  var colorArray = [DvtButtonLAFUtils.GRADIENT_LIGHT, DvtButtonLAFUtils.GRADIENT_DARK];
  var alphaArray = [1, 1];
  var ratioArray = [0, 1];

  var xx = Math.PI / 2;
  var yy = Math.PI / 2;

  var mc = new DvtRect(context, xx, yy, ww, hh);
  mc.setCornerRadius(DvtButtonLAFUtils.ROUND_RECT_ELLIPSE);
  mc.setFill(new DvtLinearGradientFill(rot, colorArray, alphaArray, ratioArray, [xx, yy, ww, hh]));
  mc.setStroke(new DvtSolidStroke(DvtButtonLAFUtils.BORDER_COLOR, 1, 1));
}
    
/**
 * Render the background of the vertical content bar.
 * 
 * @param s Sprite representing the content bar
 * @param w width of bar
 * @param h height of bar
 */
DvtPanZoomControlPanel.prototype.renderVertContentBarBackground = function(s, ww, hh) {
  //gradient effect
  var rot = 0;
  var colorArray = [DvtButtonLAFUtils.GRADIENT_LIGHT, DvtButtonLAFUtils.GRADIENT_DARK];
  var alphaArray = [1, 1];
  var ratioArray = [0, 1];

  var xx = 0;
  var yy = 0;
  var mc = new DvtRect(context, xx, yy, ww, hh);
  mc.setCornerRadius(DvtButtonLAFUtils.ROUND_RECT_ELLIPSE);
  mc.setFill(new DvtLinearGradientFill(rot, colorArray, alphaArray, ratioArray, [xx, yy, ww, hh]));
  mc.setStroke(new DvtSolidStroke(DvtButtonLAFUtils.BORDER_COLOR, 1, 1));
}

/**
 * Adjust the collapse and expand arrow icons for BiDi.
 * 
 * @param dispObjCon display object containing the arrow icon
 * @param offset amount to translate arrow icon
 */
DvtPanZoomControlPanel.prototype.adjustArrowsIconForBiDi = function(dispObjCon, offset) {
  var loader = this.findLoader(dispObjCon);
  if (loader) {
    loader.setTranslateX(loader.getTranslateX() + offset);
  }
};

/**
 * Find a Loader object in the given container.
 * 
 * @param dispObjCon container to look for a Loader object in
 * 
 * @return Loader object found, or null if none found
 */
DvtPanZoomControlPanel.prototype.findLoader = function(dispObjCon) {
  var childCount = dispObjCon.getNumChildren();
  var dispObj;
  for (var i = 0; i < childCount; i++) {
    dispObj = dispObjCon.getChildAt(i);
    if (dispObj instanceof DvtImage) {
      return dispObj;
    }
    else if (dispObj instanceof DvtContainer) {
      return this.findLoader(dispObj);
    }
  }
  return null;
};

DvtPanZoomControlPanel.prototype.setEnabled = function(enabled) {
  this._enabled = enabled;
  if (enabled) {
    this.addEventListener("mouseover", this._mouseOverHandler, false, this);
    this.addEventListener("mouseout", this._mouseOutHandler, false, this);
    this.addEventListener("mousedown", this._mouseDownHandler, false, this);
    this.addEventListener("mouseup", this._mouseUpHandler, false, this);

    // this.addEventListener("click", this._clickHandler, false, this);
  }
  else {
    this.removeEventListener("mouseover", this._mouseOverHandler, false, this);
    this.removeEventListener("mouseout", this._mouseOutHandler, false, this);
    this.removeEventListener("mousedown", this._mouseDownHandler, false, this);
    this.removeEventListener("mouseup", this._mouseUpHandler, false, this);

    // this.removeEventListener("click", this._clickHandler, false, this);
  }
};

DvtPanZoomControlPanel.prototype._addMouseRollOverListeners = function() {
  this.addEventListener("mouseover", this.HandleRollOver, false, this);
  this.addEventListener("mouseout", this.HandleRollOut, false, this);
};


DvtPanZoomControlPanel.prototype._removeMouseRollOverListeners = function() {
  this.removeEventListener("mouseover", this.HandleRollOver, false, this);
  this.removeEventListener("mouseout", this.HandleRollOut, false, this);
};


// Mouse event handling
DvtPanZoomControlPanel.prototype.HandleRollOver = function(event) {
  this._bMouseOver = true;
  if (!this._bMouseDragPanning)
  {
    this._applyAlphasRollover();
  }
};

DvtPanZoomControlPanel.prototype.HandleRollOut = function(event) {
  this._bMouseOver = false;
  if (!this._bMouseDragPanning)
  {
    this._applyAlphasRollout();
  }
};

/**
 * Apply alphas to the background and frame for when the
 * mouse is over the control panel.
 */
DvtPanZoomControlPanel.prototype._applyAlphasRollover = function() {
  this._background.setAlpha(DvtPanZoomControlPanel.ALPHA_ROLLOVER_BACKGROUND);
  this._frame.setAlpha(DvtPanZoomControlPanel.ALPHA_ROLLOVER_FRAME);
    
  //BUG FIX #10154856: fade underlay just like control panel fill
  if (this._panControlUnderlay) {
    this._panControlUnderlay.setAlpha(DvtPanZoomControlPanel.ALPHA_ROLLOVER_BACKGROUND);
  }
};

/**
 * Apply alphas to the background and frame for when the
 * mouse is not over the control panel.
 */
DvtPanZoomControlPanel.prototype._applyAlphasRollout = function() {
  this._background.setAlpha(DvtPanZoomControlPanel.ALPHA_ROLLOUT_BACKGROUND);
  this._frame.setAlpha(DvtPanZoomControlPanel.ALPHA_ROLLOUT_FRAME);
    
  //BUG FIX #10154856: fade underlay just like control panel fill
  if (this._panControlUnderlay) {
    this._panControlUnderlay.setAlpha(DvtPanZoomControlPanel.ALPHA_ROLLOUT_BACKGROUND);
  }
};


/**
 * Determine if the zoom slider is supposed to be included in the control panel.
 * 
 * @return true if the zoom slider is supposed to be included, false if not
 */
DvtPanZoomControlPanel.prototype.isZoomSliderShown = function() {
  //  return (this._controls & DvtPanZoomControlPanel.CONTROLS_ZOOM_SLIDER) > 0;
  return false;
}

/**
 * @override
 */
DvtPanZoomControlPanel.prototype.getDimensions = function() {
  if (! this._dim) {
    this._dim = DvtPanZoomControlPanel.superclass.getDimensions.call(this);
  }
  return this._dim;
}

/**
 * ControlPanel for use with Diagram.
 * This panel contains tools for zooming, panning, and changing layout.
 * The panel is initially collapsed, but can be expanded by the user.
 */
var DvtControlPanel = function (context, view) {
  this.Init(context, view);
};

/*
 * make DvtControlPanel a subclass of DvtPanZoomControlPanel
 */
DvtObj.createSubclass(DvtControlPanel, DvtPanZoomControlPanel, "DvtControlPanel");

/*--------------------------------------------------------------------*/
/*  Init()                                                            */
/*--------------------------------------------------------------------*/
/**
 * Constructor
 *
 * @param view Diagram this ControlPanel is associated with
 */
DvtControlPanel.prototype.Init = function (context, view) {
  DvtControlPanel.superclass.Init.call(this, context, view.getPanZoomCanvas(),
                                       view.getPanZoomResources(), 0);
  /* TODO
    ((view.isPanningOff() && view.isZoomingOff() && view.isZoomToFitOff()) ? 
      (CONTROLS_ALL & (~(CONTROLS_CENTER_BUTTON | CONTROLS_ZOOM | CONTROLS_ZOOM_TO_FIT_BUTTON))) : 
        (view.isPanningOff() && view.isZoomingOff()) ? (CONTROLS_ALL & (~(CONTROLS_CENTER_BUTTON | CONTROLS_ZOOM))) : 
          (view.isPanningOff() && view.isZoomToFitOff()) ? (CONTROLS_ALL & (~(CONTROLS_CENTER_BUTTON | CONTROLS_ZOOM_TO_FIT_BUTTON))) : 
            (view.isZoomingOff() && view.isZoomToFitOff()) ? (CONTROLS_ALL & (~(CONTROLS_ZOOM | CONTROLS_ZOOM_TO_FIT_BUTTON))) : 
              (view.isPanningOff() ? (CONTROLS_ALL & (~CONTROLS_CENTER_BUTTON)) : 
                (view.isZoomToFitOff() ? (CONTROLS_ALL & (~CONTROLS_ZOOM_TO_FIT_BUTTON)) : 
                  (view.isZoomingOff() ? (CONTROLS_ALL & (~CONTROLS_ZOOM)) : CONTROLS_ALL)))));
  */
  this._view = view;
}


DvtControlPanel.prototype.PopulateHorzContentBar = function (contentSprite) {
  DvtControlPanel.superclass.PopulateHorzContentBar.call(this, contentSprite);

  if (! this._view.isChangeLayoutOff()) {

  //only create the layout combo box if the current layout
  //is provided as an option in the dropdown

    //create the layout combo box
    this._layoutComboBox = this.CreateLayoutComboBox();
    contentSprite.addChild(this._layoutComboBox);

    this._layoutComboBox.setTranslateX(0);//50;//+= currX;
    this._layoutComboBox.setTranslateY(22);//24;

  }
}

/**
 * Create the combo box for changing the layout.
 *
 * @return ComboBox for changing layout
 */
DvtControlPanel.prototype.CreateLayoutComboBox = function () {

  var context = this.getContext();
  var layoutCombo = new DvtComboBox(context);
  var resources = this._view.getResources();
  var tooltip;

  //order in which items are added determines indexes of layouts
  //for combo box change event handling
  if (resources) {
    tooltip = resources.getLayoutHierVertTopTooltip();
  }
  var itemSprite = new DvtContainer(context);
  //itemState id = iconName;
  var itemButton = new DvtButton(context,
                       DvtControlPanelLAFUtils.createLayoutItemState(context, DvtImageLAFUtils.STATE_ENABLED,  
                                                                     DvtDiagram.LAYOUT_HIER_VERT_TOP),
                       DvtControlPanelLAFUtils.createLayoutItemState(context, DvtImageLAFUtils.STATE_OVER, 
                                                                     DvtDiagram.LAYOUT_HIER_VERT_TOP),
                       DvtControlPanelLAFUtils.createLayoutItemState(context, DvtImageLAFUtils.STATE_DOWN, 
                                                                     DvtDiagram.LAYOUT_HIER_VERT_TOP),
                                 "hier_vert_top");

  itemSprite.addChild(itemButton);

  //buttonState id = iconName + "sel"
  layoutCombo.addItem(itemSprite, tooltip,
                      DvtControlPanelLAFUtils.createLayoutButtonState(context, 
                                                                      DvtImageLAFUtils.STATE_ENABLED, 
                                                                      DvtDiagram.LAYOUT_HIER_VERT_TOP), 
                      DvtControlPanelLAFUtils.createLayoutButtonState(context, 
                                                                      DvtImageLAFUtils.STATE_OVER, 
                                                                      DvtDiagram.LAYOUT_HIER_VERT_TOP), 
                      DvtControlPanelLAFUtils.createLayoutButtonState(context, 
                                                                      DvtImageLAFUtils.STATE_DOWN, 
                                                                      DvtDiagram.LAYOUT_HIER_VERT_TOP));

  if (resources) {
    tooltip = resources.getLayoutHierHorzLeftTooltip();
  }
  itemSprite = new DvtContainer(context);
  itemButton = new DvtButton(context,
                   DvtControlPanelLAFUtils.createLayoutItemState(context, DvtImageLAFUtils.STATE_ENABLED, 
                                                                 DvtDiagram.LAYOUT_HIER_HORZ_LEFT),
                   DvtControlPanelLAFUtils.createLayoutItemState(context, DvtImageLAFUtils.STATE_OVER, 
                                                                 DvtDiagram.LAYOUT_HIER_HORZ_LEFT),
                   DvtControlPanelLAFUtils.createLayoutItemState(context, DvtImageLAFUtils.STATE_DOWN, 
                                                                 DvtDiagram.LAYOUT_HIER_HORZ_LEFT),
                             "hier_horz_left");
  itemSprite.addChild(itemButton);
  layoutCombo.addItem(itemSprite, tooltip,
                      DvtControlPanelLAFUtils.createLayoutButtonState(context, DvtImageLAFUtils.STATE_ENABLED, 
                                                                      DvtDiagram.LAYOUT_HIER_HORZ_LEFT), 
                      DvtControlPanelLAFUtils.createLayoutButtonState(context, 
                                                                      DvtImageLAFUtils.STATE_OVER, 
                                                                      DvtDiagram.LAYOUT_HIER_HORZ_LEFT), 
                      DvtControlPanelLAFUtils.createLayoutButtonState(context, 
                                                                      DvtImageLAFUtils.STATE_DOWN, 
                                                                      DvtDiagram.LAYOUT_HIER_HORZ_LEFT));
  if (resources) {
    tooltip = resources.getLayoutTreeTooltip();
  }
  itemSprite = new DvtContainer(context);
  itemButton = new DvtButton(context,
                   DvtControlPanelLAFUtils.createLayoutItemState(context, DvtImageLAFUtils.STATE_ENABLED, 
                                                                 DvtDiagram.LAYOUT_TREE),
                   DvtControlPanelLAFUtils.createLayoutItemState(context, DvtImageLAFUtils.STATE_OVER, 
                                                                 DvtDiagram.LAYOUT_TREE),
                   DvtControlPanelLAFUtils.createLayoutItemState(context, DvtImageLAFUtils.STATE_DOWN, 
                                                                 DvtDiagram.LAYOUT_TREE),
                             "tree");
  itemSprite.addChild(itemButton);
  layoutCombo.addItem(itemSprite, tooltip,
                      DvtControlPanelLAFUtils.createLayoutButtonState(context, 
                                                                      DvtImageLAFUtils.STATE_ENABLED, 
                                                                      DvtDiagram.LAYOUT_TREE), 
                      DvtControlPanelLAFUtils.createLayoutButtonState(context, 
                                                                      DvtImageLAFUtils.STATE_OVER, 
                                                                      DvtDiagram.LAYOUT_TREE), 
                      DvtControlPanelLAFUtils.createLayoutButtonState(context, 
                                                                      DvtImageLAFUtils.STATE_DOWN, 
                                                                      DvtDiagram.LAYOUT_TREE));
  if (resources) {
    tooltip = resources.getLayoutRadialTooltip();
  }
  itemSprite = new DvtContainer(context);
  itemButton = new DvtButton(context,
                   DvtControlPanelLAFUtils.createLayoutItemState(context, DvtImageLAFUtils.STATE_ENABLED, 
                                                                 DvtDiagram.LAYOUT_RADIAL),
                   DvtControlPanelLAFUtils.createLayoutItemState(context, DvtImageLAFUtils.STATE_OVER, 
                                                                 DvtDiagram.LAYOUT_RADIAL),
                   DvtControlPanelLAFUtils.createLayoutItemState(context, DvtImageLAFUtils.STATE_DOWN, 
                                                                 DvtDiagram.LAYOUT_RADIAL),
                             "radial");
  itemSprite.addChild(itemButton);
  layoutCombo.addItem(itemSprite, tooltip,
                      DvtControlPanelLAFUtils.createLayoutButtonState(context, 
                                                                      DvtImageLAFUtils.STATE_ENABLED, 
                                                                      DvtDiagram.LAYOUT_RADIAL), 
                      DvtControlPanelLAFUtils.createLayoutButtonState(context, 
                                                                      DvtImageLAFUtils.STATE_OVER, 
                                                                      DvtDiagram.LAYOUT_RADIAL), 
                      DvtControlPanelLAFUtils.createLayoutButtonState(context, 
                                                                      DvtImageLAFUtils.STATE_DOWN, 
                                                                      DvtDiagram.LAYOUT_RADIAL));
  if (resources) {
    tooltip = resources.getLayoutCircleTooltip();
  }
  itemSprite = new DvtContainer(context);
  itemButton = new DvtButton(context,
                   DvtControlPanelLAFUtils.createLayoutItemState(context, DvtImageLAFUtils.STATE_ENABLED, 
                                                                 DvtDiagram.LAYOUT_CIRCLE),
                   DvtControlPanelLAFUtils.createLayoutItemState(context, DvtImageLAFUtils.STATE_OVER, 
                                                                 DvtDiagram.LAYOUT_CIRCLE),
                   DvtControlPanelLAFUtils.createLayoutItemState(context, DvtImageLAFUtils.STATE_DOWN, 
                                                                 DvtDiagram.LAYOUT_CIRCLE),
                             "circle");
  itemSprite.addChild(itemButton);
  layoutCombo.addItem(itemSprite, tooltip,
                      DvtControlPanelLAFUtils.createLayoutButtonState(context, 
                                                                      DvtImageLAFUtils.STATE_ENABLED, 
                                                                      DvtDiagram.LAYOUT_CIRCLE), 
                      DvtControlPanelLAFUtils.createLayoutButtonState(context, 
                                                                      DvtImageLAFUtils.STATE_OVER, 
                                                                      DvtDiagram.LAYOUT_CIRCLE), 
                      DvtControlPanelLAFUtils.createLayoutButtonState(context, 
                                                                      DvtImageLAFUtils.STATE_DOWN, 
                                                                      DvtDiagram.LAYOUT_CIRCLE));

  layoutCombo.addEventListener("mouseover", this.HandleLayoutRollOver, false, this);
  layoutCombo.addEventListener("mouseout", this.HandleLayoutRollOut, false, this);

  //set the selected index based on the current view layout
  var iLayout = this.ConvertLayoutStringToIndex(this._view.getLayout());
  if (iLayout >  - 1)
    layoutCombo.setSelectedIndex(iLayout);

  //add the listener after setting the selected index so
  //we don't get an event right now
  layoutCombo.addEventListener(DvtComboBoxEvent.TYPE, this.HandleLayoutChange, false, this);

  //BUG FIX #8850903: listen for layout changes on the view
  //TODO???
//   this._view.addEventListener(DvtDiagram.LAYOUT_CHANGE_EVENT, this.HandleViewLayoutChange, false, this);

  return layoutCombo;
}

/**
 * Convert a layout string to its index in the combo box.
 *
 * @param sLayout layout string
 *
 * @return index of the layout in the combo box, or -1 if not found
 */
DvtControlPanel.prototype.ConvertLayoutStringToIndex = function (sLayout) {
  //set the selected index based on the current view layout
  switch (sLayout) {
    case DvtDiagram.LAYOUT_HIER_VERT_TOP:
      return 0;
    case DvtDiagram.LAYOUT_HIER_HORZ_LEFT:
      return 1;
    case DvtDiagram.LAYOUT_TREE:
      return 2;
    case DvtDiagram.LAYOUT_RADIAL:
      return 3;
    case DvtDiagram.LAYOUT_CIRCLE:
      return 4;
    default :
      return  - 1;
  }
}

/**
 *this.Handle a mouse roll over on the layout combo box.
 *
 * @param event MouseEvent
 */
DvtControlPanel.prototype.HandleLayoutRollOver = function (event) {
  var tooltip;
  if (this._view.getResources()) {
    tooltip = this._view.getResources().getLayoutTooltip();
  }
  //TODO:
  else {
    tooltip = "Layout";
  }
  this.getContext().getTooltipManager().showTooltip(tooltip, event.target);
}

/**
 *this.Handle a mouse roll out on the layout combo box.
 *
 * @param event MouseEvent
 */
DvtControlPanel.prototype.HandleLayoutRollOut = function (event) {
  this.getContext().getTooltipManager().hideTooltip();
}

/**
 *this.Handle a layout combo box event.
 *
 * @param event change ComboBoxEvent
 */
DvtControlPanel.prototype.HandleLayoutChange = function (event) {
  //BUG FIX #8850903: onlythis.Handle the event if we're not already responding
  //to a layout change
  if (! this._bDuringLayoutChange) {
    //BUG FIX #8850903: toggle flag indicating we're responding to a layout change
    this._bDuringLayoutChange = true;

    //the index of the selected item is determined by the order in which
    //the items were added to the ComboBox in CreateLayoutComboBox() method above
    this._view.doLayoutChange(event.getIndex());

    //BUG FIX #8850903: reset flag after responding to layout change
    this._bDuringLayoutChange = false;
  }
}

/**
 *this.Handle a layout change event from the view.
 *
 * @param event layout change event object
 */
DvtControlPanel.prototype.HandleViewLayoutChange = function (event) {
  //BUG FIX #8850903: onlythis.Handle the event if we're not already responding
  //to a layout change
  if (!this._bDuringLayoutChange && this._view) {
    var iLayout = this.ConvertLayoutStringToIndex(this._view.getLayout());
    if (iLayout >  - 1 && this._layoutComboBox) {
      //BUG FIX #8850903: toggle flag indicating we're responding to a layout change
      this._bDuringLayoutChange = true;

      this._layoutComboBox.setSelectedIndex(iLayout);

      //BUG FIX #8850903: reset flag after responding to layout change
      this._bDuringLayoutChange = false;
    }
  }
}

/**
 * release resources.
 */
DvtControlPanel.prototype.DestroyExpandedContent = function () {
  DvtControlPanel.superclass.DestroyExpandedContent.call(this);

  var combo = this._layoutComboBox;
  if (combo) {
    combo.removeEventListener("mouseover", this.HandleLayoutRollOver);
    combo.removeEventListener("mouseout", this.HandleLayoutRollOut);
    combo.removeEventListener(DvtComboBoxEvent.TYPE, this.HandleLayoutChange);
    this._layoutComboBox = null;

    //BUG FIX #8850903: remove the layout change listener from the view
    if (this._view) {
      this._view.removeEventListener(DvtDiagram.LAYOUT_CHANGE_EVENT, this.HandleViewLayoutChange, false);
    }
  }
}

/*
 * DvtImageLAFUtils Utility class for image
 */
var DvtImageLAFUtils = {
};

DvtImageLAFUtils.STATE_ENABLED = 0;
DvtImageLAFUtils.STATE_OVER = 1;
DvtImageLAFUtils.STATE_DOWN = 2;

DvtImageLAFUtils.loadIcon = function (context, uri) {
  var image = new DvtImage(context, uri);
  image.setMouseEnabled(false);

  DvtImageLoader.loadImage(context, uri, function (imgDim) {
    // set image size
    if (imgDim != null && imgDim.width && imgDim.height) {
      image.setWidth(imgDim.width);
      image.setHeight(imgDim.height);
    }
  });

  return image;
}
/*
 * DvtButtonLAFUtils Utility class for providing LAF for buttons in the control panel.
 */
var DvtButtonLAFUtils = {};

DvtButtonLAFUtils.CONTROL_BUTTON_WIDTH = 23;
DvtButtonLAFUtils.CONTROL_BUTTON_HEIGHT = 21;
DvtButtonLAFUtils.ZOOM_BUTTON_HEIGHT = 16;

DvtButtonLAFUtils.VIEW_PANEL_HEIGHT = 47;
DvtButtonLAFUtils.VIEW_PANEL_HALF_HEIGHT = 26;

DvtButtonLAFUtils.SIN_PI_4 = Math.sin(Math.PI / 4);
DvtButtonLAFUtils.TAN_PI_8 = Math.tan(Math.PI / 8);

DvtButtonLAFUtils.BORDER_COLOR = '#7C8191';
DvtButtonLAFUtils.GRADIENT_DARK = '#E0E1E1';
DvtButtonLAFUtils.GRADIENT_LIGHT = '#F0F1F2';
DvtButtonLAFUtils.ROUND_RECT_ELLIPSE = 8;

//Button image xml attribute names
DvtButtonLAFUtils._ZOOMIN_ENA = "zoominUp";
DvtButtonLAFUtils._ZOOMIN_OVR = "zoominOver";
DvtButtonLAFUtils._ZOOMIN_DWN = "zoominDown";
DvtButtonLAFUtils._ZOOMTOFIT_ENA = "zoomtofitUp";
DvtButtonLAFUtils._ZOOMTOFIT_OVR = "zoomtofitOver";
DvtButtonLAFUtils._ZOOMTOFIT_DWN = "zoomtofitDown";
DvtButtonLAFUtils._ZOOMOUT_ENA = "zoomoutUp";
DvtButtonLAFUtils._ZOOMOUT_OVR = "zoomoutOver";
DvtButtonLAFUtils._ZOOMOUT_DWN = "zoomoutDown";
DvtButtonLAFUtils._PAN_ENA = "pandialUp";
DvtButtonLAFUtils._PAN_OVR = "pandialOver"; 
DvtButtonLAFUtils._PAN_DWN = "pandialDown";
DvtButtonLAFUtils._RECENTER_ENA = "recenterUp"; 
DvtButtonLAFUtils._RECENTER_OVR = "recenterOver";
DvtButtonLAFUtils._RECENTER_DWN = "recenterDown";
DvtButtonLAFUtils._RESET_ENA = "resetUp"; 
DvtButtonLAFUtils._RESET_OVR = "resetOver";
DvtButtonLAFUtils._RESET_DWN = "resetDown";
DvtButtonLAFUtils._DRILLUP_ENA = "drillupUp"; 
DvtButtonLAFUtils._DRILLUP_OVR = "drillupOver";
DvtButtonLAFUtils._DRILLUP_DWN = "drillupDown";
DvtButtonLAFUtils._DRILLDOWN_ENA = "drilldownUp"; 
DvtButtonLAFUtils._DRILLDOWN_OVR = "drilldownOver";
DvtButtonLAFUtils._DRILLDOWN_DWN = "drilldownDown";

DvtButtonLAFUtils._COLLAPSE_ENA = "collapseEna"; 
DvtButtonLAFUtils._COLLAPSE_OVR = "collapseOvr";
DvtButtonLAFUtils._COLLAPSE_DWN = "collapseDwn";
DvtButtonLAFUtils._EXPAND_ENA = "expandEna"; 
DvtButtonLAFUtils._EXPAND_OVR = "expandOvr";
DvtButtonLAFUtils._EXPAND_DWN = "expandDwn"; 
//TODO
DvtButtonLAFUtils._ZOOMSLIDER_ENA = "";
DvtButtonLAFUtils._ZOOMSLIDER_OVR = "";
DvtButtonLAFUtils._ZOOMSLIDER_DWN = "";

DvtButtonLAFUtils.createPanControl = function (context, panZoomCanvas, resources, controls, imageURIs) {
  var panUpState = DvtButtonLAFUtils._createPanButtonState(context, imageURIs.getAttribute(DvtButtonLAFUtils._PAN_ENA));

  //need to offset the down state so that we can rotate the arrow around the center of the pan control
  var panDownState = new DvtContainer(context);
  panDownState.setTranslateX(20);
  panDownState.setTranslateY(20);
  var downImage = DvtButtonLAFUtils._createPanButtonState(context, imageURIs.getAttribute(DvtButtonLAFUtils._PAN_DWN));
  downImage.setTranslateX(- 20);
  downImage.setTranslateY(- 20);
  panDownState.addChild(downImage);

  //need to offset the over state so that we can rotate the arrow around the center of the pan control
  var panOverState = new DvtContainer(context);
  panOverState.setTranslateX(20);
  panOverState.setTranslateY(20);
  var overImage = DvtButtonLAFUtils._createPanButtonState(context, imageURIs.getAttribute(DvtButtonLAFUtils._PAN_OVR));
  overImage.setTranslateX(- 20);
  overImage.setTranslateY(- 20);
  panOverState.addChild(overImage);

  var panButton = new DvtButton(context, panUpState, panOverState, panDownState);
  
  if ((controls & DvtPanZoomControlPanel.CONTROLS_CENTER_BUTTON) > 0) {
    //center of pan control is smaller circle that can be used to center the view on the selected node
    var recenterButton = new DvtButton(context,
                                      DvtButtonLAFUtils._createRecenterButtonState(context, imageURIs.getAttribute(DvtButtonLAFUtils._RECENTER_ENA)),
                                      DvtButtonLAFUtils._createRecenterButtonState(context, imageURIs.getAttribute(DvtButtonLAFUtils._RECENTER_OVR)),
                                      DvtButtonLAFUtils._createRecenterButtonState(context, imageURIs.getAttribute(DvtButtonLAFUtils._RECENTER_DWN)));
    recenterButton.setTranslateX(9);
    recenterButton.setTranslateY(9);
  }

  return new DvtPanControl(context, panButton, recenterButton, panZoomCanvas, resources, controls);
}

/**
 * Create a sprite used in the zoom-to-fit button.
 *
 * @param state button state
 *
 * @return Sprite used in zoom-to-fit button
 */
DvtButtonLAFUtils.createDrillUpButton = function (context, mapCallback, panZoomCanvas, btype, resources, imageURIs) {
  var ena = DvtButtonLAFUtils._createButtonBaseImage(context, DvtImageLAFUtils.STATE_ENABLED, 
                              imageURIs.getAttribute(DvtButtonLAFUtils._DRILLUP_ENA), -2, 3, DvtButtonLAFUtils.CONTROL_BUTTON_WIDTH, DvtButtonLAFUtils.ZOOM_BUTTON_HEIGHT);
  var ovr = DvtButtonLAFUtils._createButtonBaseImage(context, DvtImageLAFUtils.STATE_OVER, 
                              imageURIs.getAttribute(DvtButtonLAFUtils._DRILLUP_OVR), -2, 3, DvtButtonLAFUtils.CONTROL_BUTTON_WIDTH, DvtButtonLAFUtils.ZOOM_BUTTON_HEIGHT);
  var dwn = DvtButtonLAFUtils._createButtonBaseImage(context, DvtImageLAFUtils.STATE_DOWN, 
                              imageURIs.getAttribute(DvtButtonLAFUtils._DRILLUP_DWN), -2, 3, DvtButtonLAFUtils.CONTROL_BUTTON_WIDTH, DvtButtonLAFUtils.ZOOM_BUTTON_HEIGHT);
  return new DvtMapControlButton(context, new DvtButton(context, ena, ovr, dwn), mapCallback, panZoomCanvas, btype, resources);
}

DvtButtonLAFUtils.createDrillDownButton = function (context, mapCallback, panZoomCanvas, btype, resources, imageURIs) {
  var ena = DvtButtonLAFUtils._createButtonBaseImage(context, DvtImageLAFUtils.STATE_ENABLED, 
                              imageURIs.getAttribute(DvtButtonLAFUtils._DRILLDOWN_ENA), -2, 3, DvtButtonLAFUtils.CONTROL_BUTTON_WIDTH, DvtButtonLAFUtils.CONTROL_BUTTON_HEIGHT);
  var ovr = DvtButtonLAFUtils._createButtonBaseImage(context, DvtImageLAFUtils.STATE_OVER, 
                              imageURIs.getAttribute(DvtButtonLAFUtils._DRILLDOWN_OVR), -2, 3, DvtButtonLAFUtils.CONTROL_BUTTON_WIDTH, DvtButtonLAFUtils.CONTROL_BUTTON_HEIGHT);
  var dwn = DvtButtonLAFUtils._createButtonBaseImage(context, DvtImageLAFUtils.STATE_DOWN, 
                              imageURIs.getAttribute(DvtButtonLAFUtils._DRILLDOWN_DWN), -2, 3, DvtButtonLAFUtils.CONTROL_BUTTON_WIDTH, DvtButtonLAFUtils.CONTROL_BUTTON_HEIGHT);
  return new DvtMapControlButton(context, new DvtButton(context, ena, ovr, dwn), mapCallback, panZoomCanvas, btype, resources);
}

DvtButtonLAFUtils.createResetButton = function (context, mapCallback, panZoomCanvas, btype, resources, imageURIs) {
  var ena = DvtButtonLAFUtils._createButtonBaseImage(context, DvtImageLAFUtils.STATE_ENABLED, 
                              imageURIs.getAttribute(DvtButtonLAFUtils._RESET_ENA), -2, 3, DvtButtonLAFUtils.CONTROL_BUTTON_WIDTH, DvtButtonLAFUtils.CONTROL_BUTTON_HEIGHT);
  var ovr = DvtButtonLAFUtils._createButtonBaseImage(context, DvtImageLAFUtils.STATE_OVER, 
                              imageURIs.getAttribute(DvtButtonLAFUtils._RESET_OVR), -2, 3, DvtButtonLAFUtils.CONTROL_BUTTON_WIDTH, DvtButtonLAFUtils.CONTROL_BUTTON_HEIGHT);
  var dwn = DvtButtonLAFUtils._createButtonBaseImage(context, DvtImageLAFUtils.STATE_DOWN, 
                              imageURIs.getAttribute(DvtButtonLAFUtils._RESET_DWN), -2, 3, DvtButtonLAFUtils.CONTROL_BUTTON_WIDTH, DvtButtonLAFUtils.CONTROL_BUTTON_HEIGHT);
  return new DvtMapControlButton(context, new DvtButton(context, ena, ovr, dwn), mapCallback, panZoomCanvas, btype, resources);
}

DvtButtonLAFUtils.createZoomToFitButton = function (context, panZoomCanvas, resources, imageURIs) {
  var ena = DvtButtonLAFUtils._createButtonBaseImage(context, DvtImageLAFUtils.STATE_ENABLED, 
                              imageURIs.getAttribute(DvtButtonLAFUtils._ZOOMTOFIT_ENA), 2, 3, DvtButtonLAFUtils.CONTROL_BUTTON_WIDTH, DvtButtonLAFUtils.CONTROL_BUTTON_HEIGHT);
  var ovr = DvtButtonLAFUtils._createButtonBaseImage(context, DvtImageLAFUtils.STATE_OVER, 
                              imageURIs.getAttribute(DvtButtonLAFUtils._ZOOMTOFIT_OVR), 2, 3, DvtButtonLAFUtils.CONTROL_BUTTON_WIDTH, DvtButtonLAFUtils.CONTROL_BUTTON_HEIGHT);
  var dwn = DvtButtonLAFUtils._createButtonBaseImage(context, DvtImageLAFUtils.STATE_DOWN, 
                              imageURIs.getAttribute(DvtButtonLAFUtils._ZOOMTOFIT_DWN), 2, 3, DvtButtonLAFUtils.CONTROL_BUTTON_WIDTH, DvtButtonLAFUtils.CONTROL_BUTTON_HEIGHT);
  return new DvtZoomToFitButton(context, new DvtButton(context, ena, ovr, dwn), panZoomCanvas, resources);
}



/**
 * Create a sprite used in the zoom-in button.
 *
 * @param state button state
 *
 * @return Sprite used in zoom-in button
 */
DvtButtonLAFUtils.createZoomInButton = function (context, panZoomCanvas, resources, imageURIs) {
  var ena = DvtButtonLAFUtils._createButtonBaseImage(context, DvtImageLAFUtils.STATE_ENABLED,
                              imageURIs.getAttribute(DvtButtonLAFUtils._ZOOMIN_ENA), -2, 3, DvtButtonLAFUtils.CONTROL_BUTTON_WIDTH, DvtButtonLAFUtils.ZOOM_BUTTON_HEIGHT);
  var ovr = DvtButtonLAFUtils._createButtonBaseImage(context, DvtImageLAFUtils.STATE_OVER, 
                              imageURIs.getAttribute(DvtButtonLAFUtils._ZOOMIN_OVR), -2, 3, DvtButtonLAFUtils.CONTROL_BUTTON_WIDTH, DvtButtonLAFUtils.ZOOM_BUTTON_HEIGHT);
  var dwn = DvtButtonLAFUtils._createButtonBaseImage(context, DvtImageLAFUtils.STATE_DOWN, 
                              imageURIs.getAttribute(DvtButtonLAFUtils._ZOOMIN_DWN), -2, 3, DvtButtonLAFUtils.CONTROL_BUTTON_WIDTH, DvtButtonLAFUtils.ZOOM_BUTTON_HEIGHT);
  return new DvtZoomInButton(context, new DvtButton(context, ena, ovr, dwn), panZoomCanvas, resources);
}

/**
 * Create a sprite used in the zoom-out button.
 *
 * @param state button state
 *
 * @return Sprite used in zoom-out button
 */
DvtButtonLAFUtils.createZoomOutButton = function (context, panZoomCanvas, resources, imageURIs) {
  var ena = DvtButtonLAFUtils._createButtonBaseImage(context, DvtImageLAFUtils.STATE_ENABLED, 
                              imageURIs.getAttribute(DvtButtonLAFUtils._ZOOMOUT_ENA), -3, 3, DvtButtonLAFUtils.CONTROL_BUTTON_WIDTH, DvtButtonLAFUtils.ZOOM_BUTTON_HEIGHT);
  var ovr = DvtButtonLAFUtils._createButtonBaseImage(context, DvtImageLAFUtils.STATE_OVER, 
                              imageURIs.getAttribute(DvtButtonLAFUtils._ZOOMOUT_OVR), -3, 3, DvtButtonLAFUtils.CONTROL_BUTTON_WIDTH, DvtButtonLAFUtils.ZOOM_BUTTON_HEIGHT);
  var dwn = DvtButtonLAFUtils._createButtonBaseImage(context, DvtImageLAFUtils.STATE_DOWN,
                              imageURIs.getAttribute(DvtButtonLAFUtils._ZOOMOUT_DWN), -3, 3, DvtButtonLAFUtils.CONTROL_BUTTON_WIDTH, DvtButtonLAFUtils.ZOOM_BUTTON_HEIGHT);
  return new DvtZoomOutButton(context, new DvtButton(context, ena, ovr, dwn), panZoomCanvas, resources);
}

//TODO: Used by DvtControlPanel which is not yet used anywhere.
DvtButtonLAFUtils.createZoomSliderButton = function (context, state) {
    return null;
}

DvtButtonLAFUtils.createControlButton = function (context, state) {
    return null;
}

DvtButtonLAFUtils._createControlButtonState = function (context, imageURIs) {
  if (!ww) {
    ww =  - 1;
  }

  var r = 3;
  //default to control button width if no width specified
  if (ww < 0) {
    ww = DvtButtonLAFUtils.CONTROL_BUTTON_WIDTH;
  }
  ww = ww - (2 * r);
  var hh = DvtButtonLAFUtils.CONTROL_BUTTON_HEIGHT - (2 * r);
  var x = ww + r;
  var y = hh + r;

  var xx = 0;
  var yy = 0;

  var cmds = DvtPathUtils.moveTo(x + r, y) + DvtPathUtils.lineTo(x + r, y + r) + DvtPathUtils.lineTo(x - ww, r + y);
  x = x - ww;
  y = y;

  cmds += DvtPathUtils.quadTo( - DvtButtonLAFUtils.TAN_PI_8 * r + x, r + y,  - DvtButtonLAFUtils.SIN_PI_4 * r + x, DvtButtonLAFUtils.SIN_PI_4 * r + y) + DvtPathUtils.quadTo( - r + x, DvtButtonLAFUtils.TAN_PI_8 * r + y,  - r + x, y) + DvtPathUtils.lineTo(x - r, y) + DvtPathUtils.lineTo(x - r, y - hh);
  x = x;
  y = y - hh;

  cmds += DvtPathUtils.quadTo( - r + x,  - DvtButtonLAFUtils.TAN_PI_8 * r + y,  - DvtButtonLAFUtils.SIN_PI_4 * r + x,  - DvtButtonLAFUtils.SIN_PI_4 * r + y) + DvtPathUtils.quadTo( - DvtButtonLAFUtils.TAN_PI_8 * r + x,  - r + y, x,  - r + y) + DvtPathUtils.lineTo(x,  - r + y) + DvtPathUtils.lineTo(x + ww,  - r + y);
  x = x + ww;
  y = y;

  cmds += DvtPathUtils.quadTo(DvtButtonLAFUtils.TAN_PI_8 * r + x,  - r + y, DvtButtonLAFUtils.SIN_PI_4 * r + x,  - DvtButtonLAFUtils.SIN_PI_4 * r + y) + DvtPathUtils.quadTo(r + x,  - DvtButtonLAFUtils.TAN_PI_8 * r + y, r + x, y) + DvtPathUtils.lineTo(x + r, y + hh) + DvtPathUtils.closePath();

  var shape = new DvtPath(context, cmds);
  DvtButtonLAFUtils._setButtonColors(state, shape, ww, hh + (r * 2), xx, yy);

  if (bname) {
    var mc = DvtImageLAFUtils.loadIcon(context, imageURIs.getAttribute(""));
    shape.addChild(mc);
  }

  return shape;
}

DvtButtonLAFUtils.createExpandButton = function (context, imageURIs, h) {
  var ena = DvtButtonLAFUtils._createCollapseExpandButtonState(context, DvtImageLAFUtils.STATE_ENABLED, h, imageURIs.getAttribute(DvtButtonLAFUtils._EXPAND_ENA));
  var ovr = DvtButtonLAFUtils._createCollapseExpandButtonState(context, DvtImageLAFUtils.STATE_OVER, h, imageURIs.getAttribute(DvtButtonLAFUtils._EXPAND_OVR));
  var dwn = DvtButtonLAFUtils._createCollapseExpandButtonState(context, DvtImageLAFUtils.STATE_DOWN, h, imageURIs.getAttribute(DvtButtonLAFUtils._EXPAND_DWN));
  return new DvtButton(context, ena, ovr, dwn);
}

DvtButtonLAFUtils.createCollapseButton = function (context, imageURIs, h) {
  var ena = DvtButtonLAFUtils._createCollapseExpandButtonState(context, DvtImageLAFUtils.STATE_ENABLED, h, imageURIs.getAttribute(DvtButtonLAFUtils._COLLAPSE_ENA));
  var ovr = DvtButtonLAFUtils._createCollapseExpandButtonState(context, DvtImageLAFUtils.STATE_OVER, h, imageURIs.getAttribute(DvtButtonLAFUtils._COLLAPSE_OVR));
  var dwn = DvtButtonLAFUtils._createCollapseExpandButtonState(context, DvtImageLAFUtils.STATE_DOWN, h, imageURIs.getAttribute(DvtButtonLAFUtils._COLLAPSE_DWN));
  return new DvtButton(context, ena, ovr, dwn);
}

DvtButtonLAFUtils._createButtonBaseImage = function (context, state, uri, translate, r, w, h) {
  var base = DvtButtonLAFUtils._drawBaseButton(context, state, r, w, h);
  var image = DvtImageLAFUtils.loadIcon(context, uri);

  //center icon in button
  if (image) {
    base.addChild(image);
    image.setTranslateY(translate);
  }
  return base;
}

/**
 * Create a sprite used in the zoom slider thumb.
 *
 * @param state button state
 *
 * @return Sprite used in zoom slider thumb
 */
DvtButtonLAFUtils._createZoomSliderButtonState = function (context, state, ww, uri) {
//  var ww = 23;
  var hh = 16;
  var x = ww;
  var y = hh;

  var xx = 0;
  var yy = 0;

  var cmds = DvtPathUtils.moveTo(x, y) + DvtPathUtils.lineTo(x - ww, y);
  x = x - ww;
  y = y;

  cmds += DvtPathUtils.lineTo(x, y - hh);
  x = x;
  y = y - hh;

  cmds += DvtPathUtils.lineTo(x + ww, y);
  x = x + ww;
  y = y;

  cmds += DvtPathUtils.lineTo(x, y + hh) + DvtPathUtils.closePath();

  var mc = new DvtPath(context, cmds);

  var currLoader = DvtImageLAFUtils.loadIcon(context, uri);
  if (currLoader) {
    //left-align icon
    // Bug 9506699 - BIDI: Control panel buttons should have local specific icons
    currLoader.setTranslateX( - 6 + sw);
    //center icon vertically
    currLoader.setTranslateY( - 2);
    //TODO?
    mc.addChild(currLoader);
  }

  // Line Style & fill
  DvtButtonLAFUtils._setSliderButtonColors(state, mc, ww, hh, xx, yy);

  return mc;
}

/**
 * Create a sprite used in the pan button.
 *
 * @param state button state
 *
 * @return Sprite used in pan button
 */
DvtButtonLAFUtils._createPanButtonState = function (context, uri) {
  var mc = new DvtContainer(context);
  var hitZone = new DvtCircle(context, 20, 20, 20);
  hitZone.setAlpha(0);
  hitZone.setFill(new DvtSolidFill("#FFFFFF"));
  mc.addChild(hitZone);

  var image = DvtImageLAFUtils.loadIcon(context, uri);
  if (image) {
    mc.addChild(image);
  }
  return mc;
}

/**
 * Create a sprite used in the re-center button.
 *
 * @param state button state
 *
 * @return Sprite used in re-center button
 */
DvtButtonLAFUtils._createRecenterButtonState = function (context, uri) {
  var mc = new DvtContainer(context);
  var hitZone = new DvtCircle(context, 11, 11, 8);
  hitZone.setAlpha(0);
  hitZone.setFill(new DvtSolidFill("#FFFFFF"));
  mc.addChild(hitZone);

  var image = DvtImageLAFUtils.loadIcon(context, uri);
  if (image) {
    mc.addChild(image);
  }

  return mc;
}

/**
 * Create a sprite used in the button to collapse the control panel.
 *
 * @param state button state
 * @param nh height of the close button
 *
 * @return Sprite used in collapse button
 */
DvtButtonLAFUtils._createCollapseExpandButtonState = function (context, state, nh, uri) {

  //BUG FIX #10154856: pass in height so we can show single row in
  //horizontal arm of control panel in TMap
  if (!nh)
    nh = 47;
  var sprite = DvtButtonLAFUtils._drawOpenCloseButtonHelper(context, state, nh);
  var iconLoader = DvtImageLAFUtils.loadIcon(context, uri);

  //center the icon in the button
  if (iconLoader) {
    sprite.addChild(iconLoader);

    iconLoader.setTranslateX(-6);
    //BUG FIX #10154856: calculate y coord instead of hardcoding because we may 
    //show 1 or 2 rows of controls in horizontal arm of control panel
    //(calculation should yield same as old code for 2 rows)
    //icon height is 20
    iconLoader.setTranslateY(Math.floor(nh / 2) - 10);
  }
  return sprite;
}

/**
 * Create the background for the pan button.
 *
 * @return pan button background
 */
DvtButtonLAFUtils.createPanButtonBackground = function (context) {
  var mc = DvtButtonLAFUtils._drawPanButtonBase(context)
  var r = 20;
  var ww = 2 * r;
  var hh = ww;
  var xx = 0;
  var yy = 0;
  
  mc.setStroke(new DvtSolidStroke("#FFFFFF", 1, 1));
  var line_rot = 36;// (1.2) * Math.PI;
  var line_colors = ["#8D93A5", "#E0EAEB", "#FFFFFF"];
  var line_alphas = [1, 1, 1];
  var line_ratios = [0, 125 / 255, 1];
  mc.setStroke(new DvtLinearGradientStroke(line_rot, line_colors, line_alphas, line_ratios, [xx, yy, ww, hh], 1));

  var rot = 90;// (1.5) * Math.PI;
  var fill_colors = ["#FFFFFF", "#FFFFFF", "#5A83BE", "#FFFFFF"];
  var fill_alphas = [.90, .10, 0, .70];
  var fill_ratios = [0, 105 / 255, 150 / 255, 1];
  mc.setFill(new DvtLinearGradientFill(rot, fill_colors, fill_alphas, fill_ratios, [xx, yy, ww, hh]));
  return mc;
}

DvtButtonLAFUtils.createPanButtonUnderlay = function (context) {
  var mc = DvtButtonLAFUtils._drawPanButtonBase(context)
  mc.setStroke(new DvtSolidStroke("#FFFFFF", 1, 1));
  mc.setFill(new DvtSolidFill("#FFFFFF", 1));
  return mc;
}

DvtButtonLAFUtils._drawPanButtonBase = function (context) {
  var r = 20;
  var ww = 2 * r;
  var hh = ww;
  var x = r;
  var y = r;
  var xx = 0;
  var yy = 0;

  var cmds = DvtPathUtils.moveTo(x + r, y) + 
             DvtPathUtils.quadTo(r + x, DvtButtonLAFUtils.TAN_PI_8 * r + y, DvtButtonLAFUtils.SIN_PI_4 * r + x, 
                                 DvtButtonLAFUtils.SIN_PI_4 * r + y) + 
             DvtPathUtils.quadTo(DvtButtonLAFUtils.TAN_PI_8 * r + x, r + y, x, r + y) + 
             DvtPathUtils.quadTo(-DvtButtonLAFUtils.TAN_PI_8 * r + x, r + y,  -DvtButtonLAFUtils.SIN_PI_4 * r + x, 
                                 DvtButtonLAFUtils.SIN_PI_4 * r + y) + 
             DvtPathUtils.quadTo(-r + x, DvtButtonLAFUtils.TAN_PI_8 * r + y,  -r + x, y) + 
             DvtPathUtils.quadTo(-r + x,  -DvtButtonLAFUtils.TAN_PI_8 * r + y,  -DvtButtonLAFUtils.SIN_PI_4 * r + x,  
                                 -DvtButtonLAFUtils.SIN_PI_4 * r + y) + 
             DvtPathUtils.quadTo(-DvtButtonLAFUtils.TAN_PI_8 * r + x,  -r + y, x,  -r + y) + 
             DvtPathUtils.quadTo(DvtButtonLAFUtils.TAN_PI_8 * r + x,  -r + y, DvtButtonLAFUtils.SIN_PI_4 * r + x,  
                                 -DvtButtonLAFUtils.SIN_PI_4 * r + y) + 
             DvtPathUtils.quadTo(r + x,  -DvtButtonLAFUtils.TAN_PI_8 * r + y, r + x, y) + DvtPathUtils.closePath();

  return new DvtPath(context, cmds, "draw_pan_button");
}


DvtButtonLAFUtils._setGradientBorder = function (mc, ww, hh, xx, yy) {
  mc.setStroke(new DvtSolidStroke("#FFFFFF", 1, 1));
  var line_rot = 63;//TODO? (1.35) * Math.PI;
  var line_colors = ["#8D93A5", "#E0EAEB", "#FFFFFF"];
  var line_alphas = [1, 1, 1];
  var line_ratios = [0, 155/255, 1];
  mc.setStroke(new DvtLinearGradientStroke(line_rot, line_colors, line_alphas, line_ratios, [xx, yy, ww, hh], 1));
}

DvtButtonLAFUtils._setButtonColors = function (state, mc, ww, hh, xx, yy) {
  var fill_colors, fill_alphas, fill_ratios;
  var rot = 90;
  switch (state) {
    case DvtImageLAFUtils.STATE_ENABLED:
      var lineThickness = 0.25;
      mc.setStroke(new DvtSolidStroke("#FFFFFF", 0, lineThickness));
      fill_colors = ["#5B868A", "#FFFFFF", "#FFFFFF", "#5B868A"];
      fill_alphas = [.01, 0, .05, .01];
      fill_ratios = [0, 120 / 255, 130 / 255, 1];
      break;
    case DvtImageLAFUtils.STATE_OVER:
      DvtButtonLAFUtils._setGradientBorder(mc, ww, hh, xx, yy);
      fill_colors = ["#FFFFFF", "#4671B0", "#4671B0", "#FFFFFF"];
      fill_alphas = [.50, .20, .10, .70];
      fill_ratios = [0, 120 / 255, 130 / 255, 1];
      break;
    case DvtImageLAFUtils.STATE_DOWN:
      DvtButtonLAFUtils._setGradientBorder(mc, ww, hh, xx, yy);
      fill_colors = ["#E0EAEB", "#5B868A", "#4671B0"];
      fill_alphas = [.10, .30, .60];
      fill_ratios = [0, 130 / 255, 1];
      break;
    default:
      break;
  }
  mc.setFill(new DvtLinearGradientFill(rot, fill_colors, fill_alphas, fill_ratios, [xx, yy, ww, hh]));
}

DvtButtonLAFUtils._setCloseButtonColors = function (context, state, mc, ww, hh, xx, yy) {
  var rot = 0;
  mc.setStroke(new DvtSolidStroke("#FFFFFF", 1, 0.8));
  var fill_colors, fill_alphas, fill_ratios;
  
  switch (state) {
    case DvtImageLAFUtils.STATE_ENABLED:
      fill_colors = ["#FFFFFF", "#5B868A", "#5B868A"];
      fill_alphas = [0, .20, .30];
      fill_ratios = [0, 130/255, 1];
      break;
    case DvtImageLAFUtils.STATE_OVER:
      fill_colors = ["#FFFFFF", "#FFFFFF", "#4671B0", "#4671B0"];
      fill_alphas = [.10, .20, .10, .60];
      fill_ratios = [0, 120/255, 130/255, 1];
      break;
    case DvtImageLAFUtils.STATE_DOWN:
      fill_colors = ["#4671B0", "#5B868A", "#5B868A"];
      fill_alphas = [.50, .20, .40];
      fill_ratios = [0, 130/255, 1];
      break;
    default:
      break;
  }
  mc.setFill(new DvtLinearGradientFill(rot, fill_colors, fill_alphas, fill_ratios, [xx, yy, ww, hh]));
}

DvtButtonLAFUtils._setSliderButtonColors = function (state, mc, ww, hh, xx, yy) {
  var rot = 90;
  var fill_colors, fill_alphas, fill_ratios;
  
  switch (state) {
    case DvtImageLAFUtils.STATE_ENABLED:
      var lineThickness = 0.25;
      mc.setStroke(new DvtSolidStroke("#FFFFFF", 1, lineThickness));
      fill_colors = ["#FFFFFF", "#4671B0", "#E0EAEB", "#FFFFFF"];
      fill_alphas = [.60, .30, .30, .80];
      fill_ratios = [0, 125 / 255, 130 / 255, 1];
      break;
    case DvtImageLAFUtils.STATE_OVER:
      DvtButtonLAFUtils._setGradientBorder(mc, ww, hh, xx, yy);
      fill_colors = ["#4671B0", "#FFFFFF", "#FFFFFF", "#FFFFFF"];
      fill_alphas = [.40, .30, .70, 1];
      fill_ratios = [0, 70/255, 200/255, 1];
      break;
    case DvtImageLAFUtils.STATE_DOWN:
      DvtButtonLAFUtils._setGradientBorder(mc, ww, hh, xx, yy);
      fill_colors = ["#FFFFFF", "#4671B0", "#E0EAEB"];
      fill_alphas = [.60, .50, .80];
      fill_ratios = [0, 130/255, 1];
      break;
    default:
      break;
  }
  mc.setFill(new DvtLinearGradientFill(rot, fill_colors, fill_alphas, fill_ratios, [xx, yy, ww, hh]));
}



//BUG FIX #10154856: pass in height to show single row of controls in 
//horizontal arm of control panel in TMap
DvtButtonLAFUtils._drawOpenCloseButtonHelper = function (context, state, nh) {
  if (!nh)
    nh = 47;

  var r = 6;
  var ww = 10 - (2 * r);
  var hh = nh - (2 * r);
  var x = ww + r;
  var y = hh + r;
  var xx = 0;
  var yy = 0;

  var cmds = DvtPathUtils.moveTo(x + r, y) + DvtPathUtils.quadTo(r + x, DvtButtonLAFUtils.TAN_PI_8 * r + y, DvtButtonLAFUtils.SIN_PI_4 * r + x, DvtButtonLAFUtils.SIN_PI_4 * r + y) + DvtPathUtils.quadTo(DvtButtonLAFUtils.TAN_PI_8 * r + x, r + y, x, r + y) + DvtPathUtils.lineTo(x, r + y) + DvtPathUtils.lineTo(x - ww, r + y);
  x = x - ww;
  y = y;

  cmds += DvtPathUtils.lineTo(x - r, y + r);
  DvtPathUtils.lineTo(x - r, y - hh);
  x = x;
  y = y - hh;

  cmds += DvtPathUtils.lineTo(x - r, y - r) + DvtPathUtils.lineTo(x + ww, y - r);
  x = x + ww;
  y = y;

  cmds += DvtPathUtils.quadTo(DvtButtonLAFUtils.TAN_PI_8 * r + x,  - r + y, DvtButtonLAFUtils.SIN_PI_4 * r + x,  - DvtButtonLAFUtils.SIN_PI_4 * r + y) + DvtPathUtils.quadTo(r + x,  - DvtButtonLAFUtils.TAN_PI_8 * r + y, r + x, y) + DvtPathUtils.lineTo(x + r, y + hh) + DvtPathUtils.lineTo(x + r, y + hh) + DvtPathUtils.closePath();

  //Note: need to know the size
  var mc = DvtButtonLAFUtils._createPathHelper(context, cmds);
  DvtButtonLAFUtils._setCloseButtonColors(context, state, mc, 10, hh + (r * 2), xx, yy);

  return mc;
}

DvtButtonLAFUtils._drawBaseButton = function (context, state, r, ww, hh) {
  ww = ww - (2 * r);
  hh = hh - (2 * r);
  var x = ww + r;
  var y = hh + r;

  var xx = 0;
  var yy = 0;

  var cmds = DvtPathUtils.moveTo(x + r, y) + DvtPathUtils.quadTo(r + x, DvtButtonLAFUtils.TAN_PI_8 * r + y, DvtButtonLAFUtils.SIN_PI_4 * r + x, DvtButtonLAFUtils.SIN_PI_4 * r + y) + DvtPathUtils.quadTo(DvtButtonLAFUtils.TAN_PI_8 * r + x, r + y, x, r + y) + DvtPathUtils.lineTo(x, y + r) + DvtPathUtils.lineTo(x - ww, r + y);
  x = x - ww;
  y = y;

  cmds += DvtPathUtils.quadTo( - DvtButtonLAFUtils.TAN_PI_8 * r + x, r + y,  - DvtButtonLAFUtils.SIN_PI_4 * r + x, DvtButtonLAFUtils.SIN_PI_4 * r + y) + DvtPathUtils.quadTo( - r + x, DvtButtonLAFUtils.TAN_PI_8 * r + y,  - r + x, y) + DvtPathUtils.lineTo(x - r, y) + DvtPathUtils.lineTo(x - r, y - hh);
  x = x;
  y = y - hh;

  cmds += DvtPathUtils.quadTo( - r + x,  - DvtButtonLAFUtils.TAN_PI_8 * r + y,  - DvtButtonLAFUtils.SIN_PI_4 * r + x,  - DvtButtonLAFUtils.SIN_PI_4 * r + y) + DvtPathUtils.quadTo( - DvtButtonLAFUtils.TAN_PI_8 * r + x,  - r + y, x,  - r + y) + DvtPathUtils.lineTo(x,  - r + y) + DvtPathUtils.lineTo(x + ww,  - r + y);
  x = x + ww;
  y = y;

  cmds += DvtPathUtils.quadTo(DvtButtonLAFUtils.TAN_PI_8 * r + x,  - r + y, DvtButtonLAFUtils.SIN_PI_4 * r + x,  - DvtButtonLAFUtils.SIN_PI_4 * r + y) + DvtPathUtils.quadTo(r + x,  - DvtButtonLAFUtils.TAN_PI_8 * r + y, r + x, y) + DvtPathUtils.lineTo(x + r, y + hh) + DvtPathUtils.closePath();

  var mc = new DvtPath(context, cmds);
  DvtButtonLAFUtils._setButtonColors(state, mc, ww, hh + (r * 2), xx, yy);

  return mc;
}

/**
 * Draw a cross line on the zoom slider.
 *
 * @param s sprite to draw into
 * @param xx x coord for start of line
 * @param yy y coord for line
 * @param ww width of line
 */
DvtButtonLAFUtils.drawZoomSliderCrossLine = function (context, s, xx, yy, ww) {
  // Line Style & FILL
  lineStyle(1, "#656D81", 1, true);

  var cmds = DvtPathUtils.moveTo(xx, yy) + DvtPathUtils.lineTo(xx + ww, yy);

  /// white
  lineStyle(1, "#FFFFFF", 1, true);

  cmds += DvtPathUtils.moveTo(xx, yy + 1) + DvtPathUtils.lineTo(xx + ww, yy + 1);
}

/**
 * Draw the background for the dropdown from the layout or panel card sync buttons.
 *
 * @param s sprite to draw into
 * @param ww width of dropdown
 * @param hh height of dropdown
 */
DvtButtonLAFUtils.drawDropdownShape = function (context, ww, hh) {
  var r = 4;
  ww -= 2 * r;
  hh -= r;
  var x = ww + r;
  var y = (hh);

  var cmds = DvtPathUtils.moveTo(x + r, y) + DvtPathUtils.quadTo(r + x, DvtButtonLAFUtils.TAN_PI_8 * r + y, DvtButtonLAFUtils.SIN_PI_4 * r + x, DvtButtonLAFUtils.SIN_PI_4 * r + y) + DvtPathUtils.quadTo(DvtButtonLAFUtils.TAN_PI_8 * r + x, r + y, x, r + y) + DvtPathUtils.lineTo(x, r + y) + DvtPathUtils.lineTo(x - ww, r + y);

  x = x - ww;
  y = y;
  cmds += DvtPathUtils.quadTo( - DvtButtonLAFUtils.TAN_PI_8 * r + x, r + y,  - DvtButtonLAFUtils.SIN_PI_4 * r + x, DvtButtonLAFUtils.SIN_PI_4 * r + y) + DvtPathUtils.quadTo( - r + x, DvtButtonLAFUtils.TAN_PI_8 * r + y,  - r + x, y) + DvtPathUtils.lineTo( - r + x, y) + DvtPathUtils.lineTo( - r + x, y - hh);

  x = x;
  y = y - hh;

  cmds += DvtPathUtils.lineTo(x + ww + r, y) + DvtPathUtils.lineTo(x + ww + r, y + hh) + DvtPathUtils.closePath();

  var shape = new DvtPath(context, cmds);
  return shape;
}

/**
 * Draw the background for the dropdown from the layout or panel card sync buttons.
 *
 * @param s sprite to draw into
 * @param ww width of dropdown
 * @param hh height of dropdown
 */
DvtButtonLAFUtils.drawDropdownShape = function (context, ww, hh) {
  var r = 4;
  ww -= 2 * r;
  hh -= r;
  var x = ww + r;
  var y = (hh);

  var cmds = DvtPathUtils.moveTo(x + r, y) + DvtPathUtils.quadTo(r + x, DvtButtonLAFUtils.TAN_PI_8 * r + y, DvtButtonLAFUtils.SIN_PI_4 * r + x, DvtButtonLAFUtils.SIN_PI_4 * r + y) + DvtPathUtils.quadTo(DvtButtonLAFUtils.TAN_PI_8 * r + x, r + y, x, r + y) + DvtPathUtils.lineTo(x, r + y) + DvtPathUtils.lineTo(x - ww, r + y);

  x = x - ww;
  y = y;
  cmds += DvtPathUtils.quadTo( - DvtButtonLAFUtils.TAN_PI_8 * r + x, r + y,  - DvtButtonLAFUtils.SIN_PI_4 * r + x, DvtButtonLAFUtils.SIN_PI_4 * r + y) + DvtPathUtils.quadTo( - r + x, DvtButtonLAFUtils.TAN_PI_8 * r + y,  - r + x, y) + DvtPathUtils.lineTo( - r + x, y) + DvtPathUtils.lineTo( - r + x, y - hh);

  x = x;
  y = y - hh;

  cmds += DvtPathUtils.lineTo(x + ww + r, y) + DvtPathUtils.lineTo(x + ww + r, y + hh) + DvtPathUtils.closePath();

  var shape = new DvtPath(context, cmds);
  return shape;
}

/**
 * Apply a drop shadow filter to the given sprite.
 *
 * @param t sprite to apply drop shadow filter to
 */
DvtButtonLAFUtils.dropShadowFilter = function (context, t) {
  var distance = 7;
  var angleInDegrees = 45;
  var color = "#5A83BE";
  var alpha = 0.9;
  var blurX = 14;
  var blurY = 14;
  var strength = 1;
  var quality = 3;
  var inner = false;
  var knockout = false;
  var hideObject = false;

  var filter = new DvtDropShadowFilter(distance, angleInDegrees, color, alpha, blurX, blurY, strength, quality, inner, knockout, hideObject);
  var filterArray = [];
  filterArray.push(filter);
  t.filters = filterArray;
}

/*
 * Temporarily add the display object to the stage to get dimensions.
 * Remove it from stage after done
 */
DvtButtonLAFUtils._getDimForced = function (context, obj) {
  //NOTE: if obj is button, getDimensions on its 1st child
  if (obj instanceof DvtButton) {
    obj = obj.getChildAt(0);
  }
  return DvtDisplayableUtils._getDimForced(context, obj);
}

/*
 * create the path object and save the path dimension
 */
DvtButtonLAFUtils._createPathHelper = function (context, cmds) {
  var path = new DvtPath(context, cmds);
  DvtButtonLAFUtils._getDimForced(context, path);
  return path;
}
/*
 * DvtPanZoomControlPanelLAFUtils Utility class for providing LAF for buttons in the control panel.
 */
var DvtPanZoomControlPanelLAFUtils = {};

DvtPanZoomControlPanelLAFUtils.VIEW_PANEL_HEIGHT = 47;
DvtPanZoomControlPanelLAFUtils.VIEW_PANEL_HALF_HEIGHT = 26;

DvtPanZoomControlPanelLAFUtils.SIN_PI_4 = Math.sin(Math.PI / 4);
DvtPanZoomControlPanelLAFUtils.TAN_PI_8 = Math.tan(Math.PI / 8);

DvtPanZoomControlPanelLAFUtils.getViewPanelHeight = function () {
  return DvtPanZoomControlPanelLAFUtils.VIEW_PANEL_HEIGHT;
}

DvtPanZoomControlPanelLAFUtils.getViewPanelHeight = function () {
  return DvtPanZoomControlPanelLAFUtils.VIEW_PANEL_HEIGHT;
}

/**
 * Get the height of the horizontal arm of the control panel to allow
 * showing only a single row of controls, like in TMap.
 */
DvtPanZoomControlPanelLAFUtils.getViewPanelHalfHeight = function () {
  //BUG FIX #10154856: show single row of controls in horizontal arm of control panel in TMap
  return DvtPanZoomControlPanelLAFUtils.VIEW_PANEL_HALF_HEIGHT;
}

/**
 * Create the background for the view part of the expanded control panel.
 *
 * @param nw width of the view part of the control panel
 * @param nh height of the view part of the control panel
 * @param bL2R true if the reading direction is left-to-right, so that the
 *        control panel is in the top left corner of the view
 *
 * @return background for the view part of the expanded control panel
 */
DvtPanZoomControlPanelLAFUtils.createEmptyViewOpenShape = function (context, nw, nh, bL2R) {
  //BUG FIX #10154856: pass in height to show single row of controls in 
  //horizontal arm of control panel in TMap
  if (! nw)
    nw = 86;
  if (! nh)
    nh = 47;
  if (! bL2R)
    bL2R = true;

  var r = 6;
  var ww = nw - (2 * r);
  var hh = nh - (2 * r);

  var xx = 0;
  var yy = 0;

  var mc = DvtPanZoomControlPanelLAFUtils.makeViewOpenShapeHelper(context, r, ww, hh, xx, yy, bL2R);
  mc.setFill(new DvtSolidFill("#FFFFFF"));
  mc.setStroke(new DvtSolidStroke("#FFFFFF", 0, 1));

  return mc;
}

/**
 * Create the frame for the view part of the expanded control panel.
 *
 * @param nw width of the view part of the control panel
 * @param nh height of the view part of the control panel
 * @param bL2R true if the reading direction is left-to-right, so that the
 *        control panel is in the top left corner of the view
 *
 * @return frame for the view part of the expanded control panel
 */
DvtPanZoomControlPanelLAFUtils.createEmptyViewOpenFrame = function (context, nw, nh, bL2R) {
  //BUG FIX #10154856: pass in height to show single row of controls in 
  //horizontal arm of control panel in TMap
  if (!nw)
    nw = 86;
  if (!nh)
    nh = 47;
  if (!bL2R)
    bL2R = true;

  var r = 6;
  var ww = nw - (2 * r);
  var hh = nh - (2 * r);

  var xx = 0;
  var yy = 0;

  var mc = DvtPanZoomControlPanelLAFUtils.makeViewOpenShapeHelper(context, r, ww, hh, xx, yy, bL2R);
  mc.setStroke(new DvtSolidStroke("#FFFFFF", 1, 1));

  return mc;
}

DvtPanZoomControlPanelLAFUtils.makeViewOpenShapeHelper = function (context, r, ww, hh, xx, yy, bL2R) {
  var x = ww + r;
  var y = hh + r;
  var cmds = DvtPathUtils.moveTo(x + r, y);
  //bottom right corner
  if (bL2R) {
    cmds += DvtPathUtils.quadTo(r + x, DvtPanZoomControlPanelLAFUtils.TAN_PI_8 * r + y, 
                                DvtPanZoomControlPanelLAFUtils.SIN_PI_4 * r + x, 
                                DvtPanZoomControlPanelLAFUtils.SIN_PI_4 * r + y) +
    DvtPathUtils.quadTo(DvtPanZoomControlPanelLAFUtils.TAN_PI_8 * r + x, r + y, x, r + y) + 
    DvtPathUtils.lineTo(x, r + y);
  }
  //BiDi: when control panel is on right side, bottom left corner is rounded,
  //and bottom right corner is square
  else {
    cmds += DvtPathUtils.lineTo(x + r, y + r);
  }
  cmds += DvtPathUtils.lineTo(x - ww, r + y);
  x = x - ww;
  y = y;

  //bottom left corner
  if (bL2R) {
    cmds += DvtPathUtils.lineTo(x - r, y + r);
  }
  //BiDi: when control panel is on right side, bottom left corner is rounded,
  //and bottom right corner is square
  else {
    cmds += DvtPathUtils.quadTo( - DvtPanZoomControlPanelLAFUtils.TAN_PI_8 * r + x, r + y,  
                                 - DvtPanZoomControlPanelLAFUtils.SIN_PI_4 * r + x, 
                                 DvtPanZoomControlPanelLAFUtils.SIN_PI_4 * r + y) + 
    DvtPathUtils.quadTo( - r + x, DvtPanZoomControlPanelLAFUtils.TAN_PI_8 * r + y,  - r + x, y) + 
    DvtPathUtils.lineTo(x - r, y);
  }
  cmds += DvtPathUtils.lineTo(x - r, y - hh);
  x = x;
  y = y - hh;

  //top left corner
  cmds += DvtPathUtils.quadTo( - r + x,  - DvtPanZoomControlPanelLAFUtils.TAN_PI_8 * r + y,  
                               - DvtPanZoomControlPanelLAFUtils.SIN_PI_4 * r + x,  
                               - DvtPanZoomControlPanelLAFUtils.SIN_PI_4 * r + y) + 
  DvtPathUtils.quadTo( - DvtPanZoomControlPanelLAFUtils.TAN_PI_8 * r + x,  - r + y, x,  - r + y) + 
  DvtPathUtils.lineTo(x,  - r + y) + DvtPathUtils.lineTo(x + ww,  - r + y);
  x = x + ww;
  y = y;

  //top right corner
  cmds += DvtPathUtils.quadTo(DvtPanZoomControlPanelLAFUtils.TAN_PI_8 * r + x,  - r + y, 
                              DvtPanZoomControlPanelLAFUtils.SIN_PI_4 * r + x,  
                              - DvtPanZoomControlPanelLAFUtils.SIN_PI_4 * r + y) + 
  DvtPathUtils.quadTo(r + x,  - DvtPanZoomControlPanelLAFUtils.TAN_PI_8 * r + y, r + x, y) + 
  DvtPathUtils.lineTo(x + r, y + hh) + 
  DvtPathUtils.closePath();

  return new DvtPath(context, cmds);
}

/**
 * Create the background for the collapsed control panel.
 *
 * @param nh height of the empty control panel
 *
 * @return background for the collapsed control panel
 */
DvtPanZoomControlPanelLAFUtils.createEmptyViewClosedShape = function (context, nh) {
  //BUG FIX #10154856: pass in height to show single row of controls in 
  //horizontal arm of control panel in TMap
  if (!nh)
    nh = 47;

  var r = 6;
  var ww = 10 - (2 * r);
  var hh = nh - (2 * r);

  var xx = 0;
  var yy = 0;

  var mc = DvtPanZoomControlPanelLAFUtils.makeViewClosedShapeHelper(context, r, ww, hh, xx, yy);
  mc.setFill(new DvtSolidFill("#FFFFFF"));
  mc.setStroke(new DvtSolidStroke("#FFFFFF", 0, 1));

  return mc;
}

/**
 * Create the frame for the collapsed control panel.
 *
 * @param nh height of the empty control panel
 *
 * @return frame for the collapsed control panel
 */
DvtPanZoomControlPanelLAFUtils.createEmptyViewClosedFrame = function (context, nh) {
  //BUG FIX #10154856: pass in height to show single row of controls in 
  //horizontal arm of control panel in TMap
  if (!nh)
    nh = 47;
  var r = 6;
  var ww = 10 - (2 * r);
  var hh = nh - (2 * r);

  var xx = 0;
  var yy = 0;

  var mc = DvtPanZoomControlPanelLAFUtils.makeViewClosedShapeHelper(context, r, ww, hh, xx, yy);
  mc.setStroke(new DvtSolidStroke("#FFFFFF", 1, 1));

  return mc;
}

DvtPanZoomControlPanelLAFUtils.makeViewClosedShapeHelper = function (context, r, ww, hh, xx, yy) {
  var x = ww + r;
  var y = hh + r;

  var cmds = DvtPathUtils.moveTo(x + r, y) + 
  DvtPathUtils.quadTo(r + x, DvtPanZoomControlPanelLAFUtils.TAN_PI_8 * r + y, 
                      DvtPanZoomControlPanelLAFUtils.SIN_PI_4 * r + x, 
                      DvtPanZoomControlPanelLAFUtils.SIN_PI_4 * r + y) + 
  DvtPathUtils.quadTo(DvtPanZoomControlPanelLAFUtils.TAN_PI_8 * r + x, r + y, x, r + y) + 
  DvtPathUtils.lineTo(x, r + y) + DvtPathUtils.lineTo(x - ww, r + y);
  x = x - ww;
  y = y;

  cmds += DvtPathUtils.lineTo(x - r, y + r) + DvtPathUtils.lineTo(x - r, y - hh);
  x = x;
  y = y - hh;

  cmds += DvtPathUtils.lineTo(x - r, y - r) + DvtPathUtils.lineTo(x + ww, y - r);
  x = x + ww;
  y = y;

  cmds += DvtPathUtils.quadTo(DvtPanZoomControlPanelLAFUtils.TAN_PI_8 * r + x,  - r + y, 
                              DvtPanZoomControlPanelLAFUtils.SIN_PI_4 * r + x,  
                              - DvtPanZoomControlPanelLAFUtils.SIN_PI_4 * r + y) + 
  DvtPathUtils.quadTo(r + x,  - DvtPanZoomControlPanelLAFUtils.TAN_PI_8 * r + y, r + x, y) + 
  DvtPathUtils.lineTo(x + r, y + hh) + DvtPathUtils.closePath();

  return new DvtPath(context, cmds, "cls_shape");
}

/**
 * Render the background for the zoom part of the expanded control panel.
 * 
 * @param s sprite to render into
 * @param nh height of the zoom part of the control panel
 */
DvtPanZoomControlPanelLAFUtils.renderEmptyZoomShape = function (context, nh) {
  if (!nh) {
    nh = 137;
  }
  var r = 6;
  var ww = 28 - (2 * r);
  var hh = nh + 7 - (2 * r);
  var xx = 0;
  var yy = 0;

  // Fill
  var mc = DvtPanZoomControlPanelLAFUtils.makeZoomShapeHelper(context, r, ww, hh, xx, yy);
  mc.setFill(new DvtSolidFill("#FFFFFF"));
  mc.setStroke(new DvtSolidStroke("#FFFFFF", 0, 1));

  return mc;
}

/**
 * Render the frame for the zoom part of the expanded control panel.
 *
 * @param s sprite to render into
 * @param nh height of the zoom part of the control panel
 * @param roundBottomRight bottom right corner is rounded
 */
DvtPanZoomControlPanelLAFUtils.renderEmptyZoomFrame = function (context, nh, roundBottomRight) {
  if (!nh)
    nh = 137;
  if (!roundBottomRight)
    roundBottomRight = true;

  var r = 6;
  var ww = 28 - (2 * r);
  var hh = nh + 7 - (2 * r);
  var xx = 0;
  var yy = 0;

  // Line Style
  var mc = DvtPanZoomControlPanelLAFUtils.makeZoomShapeHelper(context, r, ww, hh, xx, yy, roundBottomRight);
  mc.setStroke(new DvtSolidStroke("#FFFFFF", 1, 1));

  return mc;
}

DvtPanZoomControlPanelLAFUtils.makeZoomShapeHelper = function (context, r, ww, hh, xx, yy, roundBottomRight) {
  if (!roundBottomRight) {
    roundBottomRight = true;
  }
  var x = ww + r;
  var y = hh;

  var cmds = DvtPathUtils.moveTo(x + r, y);

  // Bug 9686175 - controlPanel looks bad when featuresOff="pan zoom cardSync changeLayout"
  if (roundBottomRight) {
    cmds += DvtPathUtils.quadTo(r + x, DvtPanZoomControlPanelLAFUtils.TAN_PI_8 * r + y, 
                                DvtPanZoomControlPanelLAFUtils.SIN_PI_4 * r + x, 
                                DvtPanZoomControlPanelLAFUtils.SIN_PI_4 * r + y) + 
    DvtPathUtils.quadTo(DvtPanZoomControlPanelLAFUtils.TAN_PI_8 * r + x, r + y, x, r + y) + 
    DvtPathUtils.lineTo(x, r + y);
  }
  else {
    cmds += DvtPathUtils.lineTo(x + r, y + r);
  }
  cmds += DvtPathUtils.lineTo(x - ww, r + y);
  x = x - ww;
  y = y;

  cmds += DvtPathUtils.quadTo( - DvtPanZoomControlPanelLAFUtils.TAN_PI_8 * r + x, r + y,  
                               - DvtPanZoomControlPanelLAFUtils.SIN_PI_4 * r + x, 
                               DvtPanZoomControlPanelLAFUtils.SIN_PI_4 * r + y) + 
  DvtPathUtils.quadTo( - r + x, DvtPanZoomControlPanelLAFUtils.TAN_PI_8 * r + y,  - r + x, y) + 
  DvtPathUtils.lineTo( - r + x, y) + DvtPathUtils.lineTo( - r + x, y - hh);
  x = x;
  y = y - hh;

  cmds += DvtPathUtils.lineTo(x + ww + r, y) + DvtPathUtils.lineTo(x + ww + r, y + hh) + DvtPathUtils.closePath();

  return new DvtPath(context, cmds);
}
/*
 * DvtControlPanelLAFUtils Utility class for providing LAF for the control panel.
 */
var DvtControlPanelLAFUtils = {
};

/*
 * Doesn't make sense in javascript
 * make DvtControlPanel a subclass of DvtButtonLAFUtils
DvtObj.createSubclass(DvtControlPanel, DvtButtonLAFUtils, "DvtControlPanel");
 */

//TODO button uris should be pased in xml and not hardcoded
DvtControlPanelLAFUtils.BUTTON_NAME_VIEW_TREE = "viewtree";
DvtControlPanelLAFUtils.BUTTON_NAME_VIEW_TREE_HORIZONTAL = "viewtree_horizontal";
DvtControlPanelLAFUtils.BUTTON_NAME_VIEW_VERTICAL = "viewtreevertical";
DvtControlPanelLAFUtils.BUTTON_NAME_VIEW_RADIAL = "viewtreeradial";
DvtControlPanelLAFUtils.BUTTON_NAME_VIEW_CIRCLE = "viewtreecircle";
DvtControlPanelLAFUtils.BUTTON_NAME_VIEW_FORCE = "viewtree_force";


/**
 * Create a sprite used in the button to change the view layout.
 * 
 * @param state button state
 * 
 * @return Sprite used in layout button 
 */
DvtControlPanelLAFUtils.createLayoutButtonState = function (context, state, layout) {
  //iconName + "sel"
  var bname = DvtControlPanelLAFUtils.ConvertLayoutToButtonIconName(layout);

  return DvtButtonLAFUtils.createControlButton(context, DvtButtonLAFUtils.BUTTON_TYPE_CONTROLS, imageURIs);
}

/**
 * Create a sprite used in the layout dropdown.
 * 
 * @param state button state
 * 
 * @return Sprite used in layout dropdown
 */
DvtControlPanelLAFUtils.createLayoutItemState = function (context, state, layout) {
  var bname = DvtControlPanelLAFUtils.ConvertLayoutToItemIconName(layout);
  return DvtButtonLAFUtils.creatLayoutButton(context,DvtButtonLAFUtils.BUTTON_TYPE_CONTROLS, bname, state);
}

/**
 * Convert the layout to the name used by the icons for the layout button.
 * 
 * @param layout view layout
 * 
 * @return name used by icons for layout button
 */
DvtControlPanelLAFUtils.ConvertLayoutToButtonIconName = function (layout) {
  var iconName = DvtControlPanelLAFUtils.ConvertLayoutToItemIconName(layout);
  if (iconName)
    iconName = iconName + "sel";
  return iconName;
}

/**
 * Convert the layout to the name used by the icons for the layout dropdown.
 * 
 * @param layout view layout
 * 
 * @return name used by icons for layout dropdown
 */
DvtControlPanelLAFUtils.ConvertLayoutToItemIconName = function (layout) {
  switch (layout) {
  case DvtDiagram.LAYOUT_HIER_VERT_TOP:
    return DvtControlPanelLAFUtils.BUTTON_NAME_VIEW_TREE;
  case DvtDiagram.LAYOUT_HIER_HORZ_LEFT:
    return DvtControlPanelLAFUtils.BUTTON_NAME_VIEW_TREE_HORIZONTAL;
  case DvtDiagram.LAYOUT_TREE:
    return DvtControlPanelLAFUtils.BUTTON_NAME_VIEW_VERTICAL;

  case DvtDiagram.LAYOUT_RADIAL:
    return DvtControlPanelLAFUtils.BUTTON_NAME_VIEW_RADIAL;
  case DvtDiagram.LAYOUT_CIRCLE:
    return DvtControlPanelLAFUtils.BUTTON_NAME_VIEW_CIRCLE;

  default:
    return null;
  }
}



/**
 * Class used to maintain translated resources used by the PanZoomControlPanel.
 */
var DvtPanZoomControlPanelResources = function () {
  this.Init();
};


/*
 * make DvtPanZoomControlPanelResources a subclass of DvtObj
 */
DvtObj.createSubclass(DvtPanZoomControlPanelResources, DvtObj, "DvtPanZoomControlPanelResources");


DvtPanZoomControlPanelResources.prototype.Init = function () {
  //control panel
  this._controlPanelExpandTooltip = "Show Control Panel (/)";
  this._controlPanelCollapseTooltip = "Hide (/)";
  this._panControlCenterTooltip = "Zoom and Center (Ctrl+Alt+0)";
  this._panControlTooltip = "Pan";
  this._zoomToFitTooltip = "Zoom to Fit (0)";
  this._zoomInTooltip = "Zoom In (+)";
  this._zoomOutTooltip = "Zoom Out (-)";
}

/**
 * Set the ControlPanelExpandTooltip to use.
 * 
 * @param controlPanelExpandToolTip to use
 */
DvtPanZoomControlPanelResources.prototype.setControlPanelExpandTooltip = function (s) {
  this._controlPanelExpandToolTip = s;
}
  
/**
 * Get the ControlPanelExpandTooltip to use.
 * 
 * @return controlPanelExpandToolTip being used
 */
DvtPanZoomControlPanelResources.prototype.getControlPanelExpandTooltip = function () {
  return this._controlPanelExpandTooltip;
}
    
/**
 * Set the ControlPanelCollapseTooltip to use.
 * 
 * @param controlPanelCollapseToolTip to use
 */
DvtPanZoomControlPanelResources.prototype.setControlPanelCollapseTooltip = function (s) {
  this._controlPanelCollapseToolTip = s;
}
  
/**
 * Get the ControlPanelCollapseTooltip to use.
 * 
 * @return controlPanelCollapseToolTip being used
 */
DvtPanZoomControlPanelResources.prototype.getControlPanelCollapseTooltip = function () {
  return this._controlPanelCollapseTooltip;
}
    
/**
 * Set the PanControlCenterTooltip to use.
 * 
 * @param panControlCenterToolTip to use
 */
DvtPanZoomControlPanelResources.prototype.setPanControlCenterTooltip = function (s) {
  this._panControlCenterToolTip = s;
}
  
/**
 * Get the PanControlCenterTooltip to use.
 * 
 * @return panControlCenterToolTip being used
 */
DvtPanZoomControlPanelResources.prototype.getPanControlCenterTooltip = function () {
  return this._panControlCenterTooltip;
}
    
/**
 * Set the PanControlTooltip to use.
 * 
 * @param panControlToolTip to use
 */
DvtPanZoomControlPanelResources.prototype.setPanControlTooltip = function (s) {
  this._panControlToolTip = s;
}
  
/**
 * Get the PanControlTooltip to use.
 * 
 * @return panControlToolTip being used
 */
DvtPanZoomControlPanelResources.prototype.getPanControlTooltip = function () {
  return this._panControlTooltip;
}
    


/**
 * Set the ZoomToFitTooltip to use.
 * 
 * @param zoomToFitToolTip to use
 */
DvtPanZoomControlPanelResources.prototype.setZoomToFitTooltip = function (s) {
  this._zoomToFitToolTip = s;
}
  
/**
 * Get the ZoomToFitTooltip to use.
 * 
 * @return zoomToFitToolTip being used
 */
DvtPanZoomControlPanelResources.prototype.getZoomToFitTooltip = function () {
  return this._zoomToFitTooltip;
}
    
/**
 * Set the ZoomInTooltip to use.
 * 
 * @param zoomInToolTip to use
 */
DvtPanZoomControlPanelResources.prototype.setZoomInTooltip = function (s) {
  this._zoomInToolTip = s;
}
  
/**
 * Get the ZoomInTooltip to use.
 * 
 * @return zoomInToolTip being used
 */

DvtPanZoomControlPanelResources.prototype.getZoomInTooltip = function () {
  return this._zoomInTooltip;
}
    
/**
 * Set the ZoomOutTooltip to use.
 * 
 * @param zoomOutToolTip to use
 */
DvtPanZoomControlPanelResources.prototype.setZoomOutTooltip = function (s) {
  this._zoomOutToolTip = s;
}
  
/**
 * Get the ZoomOutTooltip to use.
 * 
 * @return zoomOutToolTip being used
 */
DvtPanZoomControlPanelResources.prototype.getZoomOutTooltip = function () {
  return this._zoomOutTooltip;
}
    

